Browse Source

- Added steep parallax mapping, activate it by setting SteepParallax attribute to true in lighting material
- Refactored paralax calculation code in Parallax.glsllib
- Added a boolean param to lighting material PackedNormalParallax to specify if the parallax map is stored in the alpha channel of the normal map (it was already implemented in the shader). added a dds file and a material using this
- The parallax height can now be set by users by setting the ParallaxHeight attribute. default is 0.05
- Deleted old normal map for water
- Inverted green channel of the brickwall normal texture to look good with recent change in normal calculation in lighting material
- Created a test case for parallax mapping where you can swich from classic to steep parallax using spacebar, and where you can tweak the parallax heigh by using I and K keys.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8046 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

rem..om 14 years ago
parent
commit
5f5eb708da

+ 24 - 13
engine/src/core-data/Common/MatDefs/Light/Lighting.frag

@@ -1,3 +1,4 @@
+#import "Common/ShaderLib/Parallax.glsllib"
 #import "Common/ShaderLib/Optics.glsllib"
 #define ATTENUATION
 //#define HQ_ATTENUATION
@@ -30,7 +31,10 @@ varying vec3 SpecularSum;
 #endif
 
 #ifdef PARALLAXMAP
-  uniform sampler2D m_ParallaxMap;
+  uniform sampler2D m_ParallaxMap;  
+#endif
+#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) 
+    uniform float m_ParallaxHeight;
 #endif
 
 #ifdef LIGHTMAP
@@ -38,7 +42,7 @@ varying vec3 SpecularSum;
 #endif
   
 #ifdef NORMALMAP
-  uniform sampler2D m_NormalMap;
+  uniform sampler2D m_NormalMap;   
 #else
   varying vec3 vNormal;
 #endif
@@ -132,20 +136,27 @@ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 w
 void main(){
     vec2 newTexCoord;
      
-    #if (defined(PARALLAXMAP) || defined(NORMALMAP_PARALLAX)) && !defined(VERTEX_LIGHTING)
-       float h;
-       #ifdef PARALLAXMAP
-          h = texture2D(m_ParallaxMap, texCoord).r;
+    #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) 
+     
+       #ifdef STEEP_PARALLAX
+           #ifdef NORMALMAP_PARALLAX
+               //parallax map is stored in the alpha channel of the normal map         
+               newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+           #else
+               //parallax map is a texture
+               newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);         
+           #endif
        #else
-          h = texture2D(m_NormalMap, texCoord).a;
+           #ifdef NORMALMAP_PARALLAX
+               //parallax map is stored in the alpha channel of the normal map         
+               newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+           #else
+               //parallax map is a texture
+               newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
+           #endif
        #endif
-       float heightScale = 0.05;
-       float heightBias = heightScale * -0.5;
-       vec3 normView = normalize(vViewDir);
-       h = (h * heightScale + heightBias) * normView.z;
-       newTexCoord = texCoord + (h * normView.xy);
     #else
-       newTexCoord = texCoord;
+       newTexCoord = texCoord;    
     #endif
     
    #ifdef DIFFUSEMAP

+ 11 - 0
engine/src/core-data/Common/MatDefs/Light/Lighting.j3md

@@ -61,6 +61,15 @@ MaterialDef Phong Lighting {
         // Parallax/height map
         Texture2D ParallaxMap
 
+        //Set to true is parallax map is stored in the alpha channel of the normal map
+        Boolean PackedNormalParallax   
+
+        //Sets the relief height for parallax mapping
+        Float ParallaxHeight : 0.05       
+
+        //Set to true to activate Steep Parallax mapping
+        Boolean SteepParallax
+
         // Texture that specifies alpha values
         Texture2D AlphaMap
 
@@ -124,6 +133,8 @@ MaterialDef Phong Lighting {
             NORMALMAP : NormalMap
             SPECULARMAP : SpecularMap
             PARALLAXMAP : ParallaxMap
+            NORMALMAP_PARALLAX : PackedNormalParallax
+            STEEP_PARALLAX : SteepParallax
             ALPHAMAP : AlphaMap
             COLORRAMP : ColorRamp
             LIGHTMAP : LightMap

BIN
engine/src/core-data/Common/MatDefs/Water/Textures/gradient_map.jpg


+ 78 - 0
engine/src/core-data/Common/ShaderLib/Parallax.glsllib

@@ -0,0 +1,78 @@
+#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)    
+    vec2 steepParallaxOffset(sampler2D parallaxMap, vec3 vViewDir,vec2 texCoord,float parallaxScale){
+        vec2 vParallaxDirection = normalize(  vViewDir.xy );
+
+        // The length of this vector determines the furthest amount of displacement: (Ati's comment)
+        float fLength         = length( vViewDir );
+        float fParallaxLength = sqrt( fLength * fLength - vViewDir.z * vViewDir.z ) / vViewDir.z; 
+
+        // Compute the actual reverse parallax displacement vector: (Ati's comment)
+        vec2 vParallaxOffsetTS = vParallaxDirection * fParallaxLength;
+
+        // Need to scale the amount of displacement to account for different height ranges
+        // in height maps. This is controlled by an artist-editable parameter: (Ati's comment)              
+        parallaxScale *=0.3;
+        vParallaxOffsetTS *= parallaxScale;
+
+       vec3 eyeDir = normalize(vViewDir).xyz;   
+
+        float nMinSamples = 6;
+        float nMaxSamples = 1000 * parallaxScale;   
+        float nNumSamples = mix( nMinSamples, nMaxSamples, 1.0 - eyeDir.z );   //In reference shader: int nNumSamples = (int)(lerp( nMinSamples, nMaxSamples, dot( eyeDirWS, N ) ));
+        float fStepSize = 1.0 / nNumSamples;   
+        float fCurrHeight = 0.0;
+        float fPrevHeight = 1.0;
+        float fNextHeight = 0.0;
+        float nStepIndex = 0;
+        vec2 vTexOffsetPerStep = fStepSize * vParallaxOffsetTS;
+        vec2 vTexCurrentOffset = texCoord;
+        float  fCurrentBound     = 1.0;
+        float  fParallaxAmount   = 0.0;   
+
+        while ( nStepIndex < nNumSamples && fCurrHeight <= fCurrentBound ) {
+            vTexCurrentOffset -= vTexOffsetPerStep;
+            fPrevHeight = fCurrHeight;
+            
+           
+           #ifdef NORMALMAP_PARALLAX
+               //parallax map is stored in the alpha channel of the normal map         
+               fCurrHeight = texture2DLod( parallaxMap, vTexCurrentOffset,1.0).a; 
+           #else
+               //parallax map is a texture
+               fCurrHeight = texture2DLod( parallaxMap, vTexCurrentOffset,1.0).r;                
+           #endif
+           
+            fCurrentBound -= fStepSize;
+            nStepIndex+=1.0;
+        } 
+        vec2 pt1 = vec2( fCurrentBound, fCurrHeight );
+        vec2 pt2 = vec2( fCurrentBound + fStepSize, fPrevHeight );
+
+        float fDelta2 = pt2.x - pt2.y;
+        float fDelta1 = pt1.x - pt1.y;
+
+        float fDenominator = fDelta2 - fDelta1;
+
+        fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1 ) / fDenominator;
+
+        vec2 vParallaxOffset = vParallaxOffsetTS * (1.0 - fParallaxAmount );
+       return texCoord - vParallaxOffset;  
+    }
+
+    vec2 classicParallaxOffset(sampler2D parallaxMap, vec3 vViewDir,vec2 texCoord,float parallaxScale){ 
+       float h;
+       h = texture2D(parallaxMap, texCoord).a;
+       #ifdef NORMALMAP_PARALLAX
+               //parallax map is stored in the alpha channel of the normal map         
+               h = texture2D(parallaxMap, texCoord).a;               
+       #else
+               //parallax map is a texture
+               h = texture2D(parallaxMap, texCoord).r;
+       #endif
+       float heightScale = parallaxScale;
+       float heightBias = heightScale* -0.6;
+       vec3 normView = normalize(vViewDir);       
+       h = (h * heightScale + heightBias) * normView.z;
+       return texCoord + (h * normView.xy);
+    }
+#endif

+ 8 - 0
engine/src/test-data/Textures/Terrain/BrickWall/BrickWall2.j3m

@@ -0,0 +1,8 @@
+Material Pong Rock : Common/MatDefs/Light/Lighting.j3md {
+     MaterialParameters {
+         Shininess: 2.0
+         DiffuseMap :  Textures/Terrain/BrickWall/BrickWall.jpg
+         NormalMap :   Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds
+		 PackedNormalParallax: true
+     }
+}

BIN
engine/src/test-data/Textures/Terrain/BrickWall/BrickWall_normal.jpg


BIN
engine/src/test-data/Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds


+ 166 - 0
engine/src/test/jme3test/material/TestParallax.java

@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.material;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.FXAAFilter;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestParallax extends SimpleApplication {
+
+    private Vector3f lightDir = new Vector3f(-1, -1, .5f).normalizeLocal();
+
+    public static void main(String[] args) {
+        TestParallax app = new TestParallax();
+        app.start();
+    }
+
+    public void setupSkyBox() {
+        rootNode.attachChild(SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false));
+    }
+    DirectionalLight dl;
+
+    public void setupLighting() {
+
+        dl = new DirectionalLight();
+        dl.setDirection(lightDir);
+        dl.setColor(new ColorRGBA(.9f, .9f, .9f, 1));
+        rootNode.addLight(dl);
+    }
+    Material mat;
+
+    public void setupFloor() {
+        mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall2.j3m");
+        mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
+        mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat);
+        mat.setFloat("Shininess", 0);
+
+        Node floorGeom = (Node) assetManager.loadAsset("Models/WaterTest/WaterTest.mesh.xml");
+        Geometry g = ((Geometry) floorGeom.getChild(0));
+        g.getMesh().scaleTextureCoordinates(new Vector2f(10, 10));
+        TangentBinormalGenerator.generate(floorGeom);
+        floorGeom.setLocalTranslation(0, 22, 0);
+        floorGeom.setLocalScale(100);
+
+        floorGeom.setMaterial(mat);        
+        rootNode.attachChild(floorGeom);
+    }
+
+    public void setupSignpost() {
+        Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml");
+        Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m");
+        TangentBinormalGenerator.generate(signpost);
+        signpost.setMaterial(mat);
+        signpost.rotate(0, FastMath.HALF_PI, 0);
+        signpost.setLocalTranslation(12, 23.5f, 30);
+        signpost.setLocalScale(4);
+        signpost.setShadowMode(ShadowMode.CastAndReceive);
+        rootNode.attachChild(signpost);
+    }
+
+    @Override
+    public void simpleInitApp() {
+        cam.setLocation(new Vector3f(-15.445636f, 30.162927f, 60.252777f));
+        cam.setRotation(new Quaternion(0.05173137f, 0.92363626f, -0.13454558f, 0.35513034f));
+
+        flyCam.setMoveSpeed(30);
+
+
+        setupLighting();
+        setupSkyBox();
+        setupFloor();
+        setupSignpost();
+
+        inputManager.addListener(new AnalogListener() {
+
+            public void onAnalog(String name, float value, float tpf) {
+                if ("heightUP".equals(name)) {
+                    parallaxHeigh += 0.0001;
+                    mat.setFloat("ParallaxHeight", parallaxHeigh);
+                }
+                if ("heightDown".equals(name)) {
+                    parallaxHeigh -= 0.0001;
+                    parallaxHeigh = Math.max(parallaxHeigh, 0);
+                    mat.setFloat("ParallaxHeight", parallaxHeigh);
+                }
+
+            }
+        }, "heightUP", "heightDown");
+        inputManager.addMapping("heightUP", new KeyTrigger(KeyInput.KEY_I));
+        inputManager.addMapping("heightDown", new KeyTrigger(KeyInput.KEY_K));
+
+        inputManager.addListener(new ActionListener() {
+
+            public void onAction(String name, boolean isPressed, float tpf) {
+                if (isPressed && "toggleSteep".equals(name)) {
+                    steep = !steep;
+                    mat.setBoolean("SteepParallax", steep);
+                }
+            }
+        }, "toggleSteep");
+        inputManager.addMapping("toggleSteep", new KeyTrigger(KeyInput.KEY_SPACE));
+        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+        FXAAFilter fxaa = new FXAAFilter();
+        fxaa.setReduceMul(0.08f);
+        fpp.addFilter(fxaa);
+        viewPort.addProcessor(fpp);
+    }
+    float parallaxHeigh = 0.05f;
+    float time = 0;
+    boolean steep = false;
+
+    @Override
+    public void simpleUpdate(float tpf) {
+//        time+=tpf;
+//        lightDir.set(FastMath.sin(time), -1, FastMath.cos(time));
+//        bsr.setDirection(lightDir);
+//        dl.setDirection(lightDir);
+    }
+}