Преглед изворни кода

Changed the way post shadow pass is done. It's now a technique of the lighting material definition.
This allow to have shadows that fully works with partially transparent objects (like trees).
If a material does not have the postShadow technique, the renderer uses the fallback material (like before).

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

rem..om пре 13 година
родитељ
комит
bf55974bf4

+ 67 - 2
engine/src/core-data/Common/MatDefs/Light/Lighting.j3md

@@ -99,6 +99,25 @@ MaterialDef Phong Lighting {
 
         // the env map is a spheremap and not a cube map
         Boolean EnvMapAsSphereMap
+
+        //shadows
+         Int FilterMode
+        Boolean HardwareShadows
+
+        Texture2D ShadowMap0
+        Texture2D ShadowMap1
+        Texture2D ShadowMap2
+        Texture2D ShadowMap3
+        
+        Float ShadowIntensity
+        Vector4 Splits
+
+        Matrix4 LightViewProjectionMatrix0
+        Matrix4 LightViewProjectionMatrix1
+        Matrix4 LightViewProjectionMatrix2
+        Matrix4 LightViewProjectionMatrix3
+
+        Float PCFEdge
     }
 
     Technique {
@@ -141,7 +160,7 @@ MaterialDef Phong Lighting {
             SEPARATE_TEXCOORD : SeparateTexCoord
 
             USE_REFLECTION : EnvMap
-            SPHERE_MAP : SphereMap
+            SPHERE_MAP : SphereMap            
         }
     }
 
@@ -156,7 +175,8 @@ MaterialDef Phong Lighting {
         }
 
         Defines {
-            DIFFUSEMAP_ALPHA : DiffuseMap
+            COLOR_MAP : ColorMap
+            DISCARD_ALPHA : AlphaDiscardThreshold
         }
 
         RenderState {
@@ -169,6 +189,51 @@ MaterialDef Phong Lighting {
 
     }
 
+
+    Technique PostShadow15{
+        VertexShader GLSL150:   Common/MatDefs/Shadow/PostShadowPSSM.vert
+        FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            WorldMatrix
+        }
+
+        Defines {
+            HARDWARE_SHADOWS : HardwareShadows
+            FILTER_MODE : FilterMode
+            PCFEDGE : PCFEdge
+            DISCARD_ALPHA : AlphaDiscardThreshold
+            COLOR_MAP : ColorMap
+        }
+
+        RenderState {
+            Blend Modulate
+        }
+    }
+
+    Technique PostShadow{
+        VertexShader GLSL100:   Common/MatDefs/Shadow/PostShadowPSSM.vert
+        FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            WorldMatrix
+        }
+
+        Defines {
+            HARDWARE_SHADOWS : HardwareShadows
+            FILTER_MODE : FilterMode
+            PCFEDGE : PCFEdge
+            DISCARD_ALPHA : AlphaDiscardThreshold
+            COLOR_MAP : ColorMap
+        }
+
+        RenderState {
+            Blend Modulate
+        }
+    }
+
   Technique PreNormalPass {
 
         VertexShader GLSL100 :   Common/MatDefs/SSAO/normal.vert

+ 45 - 5
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag

@@ -1,6 +1,6 @@
 #ifdef HARDWARE_SHADOWS
     #define SHADOWMAP sampler2DShadow
-    #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
+    #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r 
 #else
     #define SHADOWMAP sampler2D
     #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
@@ -27,6 +27,7 @@
     #define KERNEL 8.0
 #endif
 
+
 uniform SHADOWMAP m_ShadowMap0;
 uniform SHADOWMAP m_ShadowMap1;
 uniform SHADOWMAP m_ShadowMap2;
@@ -43,7 +44,7 @@ varying vec4 projCoord3;
 
 varying float shadowPosition;
 
-const float texSize = 1024.0;
+float texSize = 1024.0;
 const float pixSize = 1.0 / texSize;
 const vec2 pixSize2 = vec2(pixSize);
 
@@ -64,6 +65,11 @@ float Shadow_BorderCheck(in vec2 coord){
 }
 
 float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
+    float border = Shadow_BorderCheck(projCoord.xy);
+    if (border > 0.0)
+        return 1.0;
+  
+
     float shadow = 0.0;
     vec2 o = mod(floor(gl_FragCoord.xy), 2.0);
     shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5,  1.5) + o);
@@ -75,6 +81,9 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
 }
 
 float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
+    float border = Shadow_BorderCheck(projCoord.xy);
+    if (border > 0.0)
+        return 1.0;
     vec4 gather = vec4(0.0);
     gather.x = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 0.0));
     gather.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0));
@@ -88,12 +97,15 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
 
 float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
     float shadow = 0.0;
+    float border = Shadow_BorderCheck(projCoord.xy);
+    if (border > 0.0)
+        return 1.0;
     float bound = KERNEL * 0.5 - 0.5;
     bound *= PCFEDGE;
     for (float y = -bound; y <= bound; y += PCFEDGE){
         for (float x = -bound; x <= bound; x += PCFEDGE){
             shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) +
-                            Shadow_BorderCheck(projCoord.xy),
+                            border,
                             0.0, 1.0);
         }
     }
@@ -102,18 +114,46 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
     return shadow;
 }
 
-void main(){
+#ifdef DISCARD_ALPHA
+    #ifdef COLOR_MAP
+        uniform sampler2D m_ColorMap;
+    #else    
+        uniform sampler2D m_DiffuseMap;
+    #endif
+    uniform float m_AlphaDiscardThreshold;
+    varying vec2 texCoord;
+#endif
+
+void main(){   
+ 
+    #ifdef DISCARD_ALPHA
+        #ifdef COLOR_MAP
+            float alpha = texture2D(m_ColorMap,texCoord).a;
+        #else    
+            float alpha = texture2D(m_DiffuseMap,texCoord).a;
+        #endif
+        if(alpha<=m_AlphaDiscardThreshold){
+            discard;
+        }
+
+    #endif
+   
+
     vec4 shadowPerSplit = vec4(0.0);
     shadowPerSplit.x = GETSHADOW(m_ShadowMap0, projCoord0);
     shadowPerSplit.y = GETSHADOW(m_ShadowMap1, projCoord1);
     shadowPerSplit.z = GETSHADOW(m_ShadowMap2, projCoord2);
     shadowPerSplit.w = GETSHADOW(m_ShadowMap3, projCoord3);
+ 
 
     vec4 less = step( shadowPosition, m_Splits );
     vec4 more = vec4(1.0) - step( shadowPosition, vec4(0.0, m_Splits.xyz) );
     float shadow = dot(shadowPerSplit, less * more );
     
     shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
-    gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
+  
+
+  gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
+  //gl_FragColor = vec4(alpha, alpha, alpha, 1.0);
 }
 

+ 8 - 1
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert

@@ -13,7 +13,12 @@ varying vec4 projCoord3;
 
 varying float shadowPosition;
 
+varying vec2 texCoord;
+
 attribute vec3 inPosition;
+#ifdef DISCARD_ALPHA
+    attribute vec2 inTexCoord;
+#endif
 
 const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
                           0.0, 0.5, 0.0, 0.0,
@@ -28,7 +33,9 @@ void main(){
     // get the vertex in world space
     vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
 
-
+    #ifdef DISCARD_ALPHA
+       texCoord = inTexCoord;
+    #endif
     // populate the light view matrices array and convert vertex to light viewProj space
     projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos;
     projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;

+ 23 - 1
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag

@@ -120,9 +120,31 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
     return shadow;
 }
 
+#ifdef DISCARD_ALPHA
+    #ifdef COLOR_MAP
+        uniform sampler2D m_ColorMap;
+    #else    
+        uniform sampler2D m_DiffuseMap;
+    #endif
+    uniform float m_AlphaDiscardThreshold;
+    varying vec2 texCoord;
+#endif
+
 void main(){
     float shadow = 0.0;
 
+    
+    #ifdef DISCARD_ALPHA
+        #ifdef COLOR_MAP
+             float alpha = texture2D(m_ColorMap,texCoord).a;
+        #else    
+             float alpha = texture2D(m_DiffuseMap,texCoord).a;
+        #endif
+      
+        if(alpha < m_AlphaDiscardThreshold){
+            discard;
+        }
+    #endif
     if(shadowPosition < m_Splits.x){
         shadow = GETSHADOW(m_ShadowMap0, projCoord0);
     }else if( shadowPosition <  m_Splits.y){
@@ -134,6 +156,6 @@ void main(){
     }
     
     shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
-    outFragColor = vec4(shadow, shadow, shadow, 1.0);
+    outFragColor =  vec4(shadow, shadow, shadow, 1.0);
 }
 

+ 17 - 5
engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag

@@ -1,14 +1,26 @@
 varying vec2 texCoord;
 
-#ifdef DIFFUSEMAP_ALPHA
-uniform sampler2D m_DiffuseMap;
+#ifdef DISCARD_ALPHA
+   #ifdef COLOR_MAP
+      uniform sampler2D m_ColorMap;
+   #else    
+      uniform sampler2D m_DiffuseMap;
+   #endif
+    uniform float m_AlphaDiscardThreshold;
 #endif
 
 
 void main(){
-   #ifdef DIFFUSEMAP_ALPHA
-      if (texture2D(m_DiffuseMap, texCoord).a <= 0.50)
-          discard;
+   #ifdef DISCARD_ALPHA
+       #ifdef COLOR_MAP
+            if (texture2D(m_ColorMap, texCoord).a <= m_AlphaDiscardThreshold){
+                discard;
+            }
+       #else    
+            if (texture2D(m_DiffuseMap, texCoord).a <= m_AlphaDiscardThreshold){
+                discard;
+            }
+       #endif
    #endif
 
    gl_FragColor = vec4(1.0);

+ 71 - 8
engine/src/core/com/jme3/shadow/PssmShadowRenderer.java

@@ -31,11 +31,13 @@ package com.jme3.shadow;
 
 import com.jme3.asset.AssetManager;
 import com.jme3.material.Material;
+import com.jme3.material.RenderState;
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.Matrix4f;
 import com.jme3.math.Vector3f;
 import com.jme3.post.SceneProcessor;
 import com.jme3.renderer.Camera;
+import com.jme3.renderer.Caps;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.ViewPort;
@@ -140,6 +142,12 @@ public class PssmShadowRenderer implements SceneProcessor {
     private Picture[] dispPic;
     private Vector3f[] points = new Vector3f[8];
     private boolean flushQueues = true;
+    //render state for post shadow pass
+    private RenderState state = new RenderState();
+    // define if the fallback material should be used.
+    private boolean needsfallBackMaterial = false;
+    //Name of the post material technique
+    private String postTechniqueName = "PostShadow";
 
     /**
      * Create a PSSM Shadow Renderer 
@@ -208,13 +216,17 @@ public class PssmShadowRenderer implements SceneProcessor {
         for (int i = 0; i < points.length; i++) {
             points[i] = new Vector3f();
         }
+
+        //initializing render state for post shadow pass (modulade blending and cullmode of for back faces )
+        state.setBlendMode(RenderState.BlendMode.Modulate);
+        state.setFaceCullMode(RenderState.FaceCullMode.Off);
     }
 
     /**
      * Sets the filtering mode for shadow edges see {@link FilterMode} for more info
      * @param filterMode 
      */
-    public void setFilterMode(FilterMode filterMode) {
+    final public void setFilterMode(FilterMode filterMode) {
         if (filterMode == null) {
             throw new NullPointerException();
         }
@@ -243,7 +255,7 @@ public class PssmShadowRenderer implements SceneProcessor {
      * sets the shadow compare mode see {@link CompareMode} for more info
      * @param compareMode 
      */
-    public void setCompareMode(CompareMode compareMode) {
+    final public void setCompareMode(CompareMode compareMode) {
         if (compareMode == null) {
             throw new NullPointerException();
         }
@@ -306,6 +318,12 @@ public class PssmShadowRenderer implements SceneProcessor {
     public void initialize(RenderManager rm, ViewPort vp) {
         renderManager = rm;
         viewPort = vp;
+        //checking for caps to chosse the appropriate post material technique
+        if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) {
+            postTechniqueName = "PostShadow15";
+        }else{
+            postTechniqueName = "PostShadow";
+        }
     }
 
     public boolean isInitialized() {
@@ -430,15 +448,24 @@ public class PssmShadowRenderer implements SceneProcessor {
     public void postFrame(FrameBuffer out) {
         Camera cam = viewPort.getCamera();
         if (!noOccluders) {
-            postshadowMat.setColor("Splits", splits);
-            for (int i = 0; i < nbSplits; i++) {
-                postshadowMat.setMatrix4("LightViewProjectionMatrix" + i, lightViewProjectionsMatrices[i]);
+            //setting params to recieving geometry list
+            setMatParams();
+            //some materials in the scene does not have a post shadow technique so we're using the fall back material
+            if (needsfallBackMaterial) {
+                renderManager.setForcedMaterial(postshadowMat);
             }
-            renderManager.setForcedMaterial(postshadowMat);
-
+            
+            //forcing the post shadow technique and render state
+            renderManager.setForcedTechnique(postTechniqueName);
+            renderManager.setForcedRenderState(state);
+            
+            //rendering the post shadow pass
             viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues);
 
+            //resetting renderManager settings
+            renderManager.setForcedTechnique(null);
             renderManager.setForcedMaterial(null);
+            renderManager.setForcedRenderState(null);
             renderManager.setCamera(cam, false);
 
         }
@@ -447,6 +474,42 @@ public class PssmShadowRenderer implements SceneProcessor {
         }
     }
 
+    private void setMatParams() {
+
+        GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive);
+        
+        //iteratin throught all the geometries of the list to set the material params
+        for (int i = 0; i < l.size(); i++) {
+            Material mat = l.get(i).getMaterial();
+            //checking if the material has the post technique and setting the params.
+            if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) {
+                mat.setColor("Splits", splits);
+                postshadowMat.setColor("Splits", splits);
+                for (int j = 0; j < nbSplits; j++) {
+                    mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
+                    mat.setTexture("ShadowMap" + j, shadowMaps[j]);
+                }
+                mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
+                mat.setInt("FilterMode", filterMode.ordinal());
+                mat.setFloat("PCFEdge", edgesThickness);
+                mat.setFloat("ShadowIntensity", shadowIntensity);
+            } else {                
+                needsfallBackMaterial = true;
+            }
+        }
+
+
+        //At least one material of the receiving geoms does not support the post shadow techniques
+        //so we fall back to the forced material solution (transparent shadows won't be supported for these objects)
+        if (needsfallBackMaterial) {
+            postshadowMat.setColor("Splits", splits);
+            for (int j = 0; j < nbSplits; j++) {
+                postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
+            }
+        }
+
+    }
+
     public void preFrame(float tpf) {
     }
 
@@ -511,7 +574,7 @@ public class PssmShadowRenderer implements SceneProcessor {
      * default is 0.7
      * @param shadowIntensity the darkness of the shadow
      */
-    public void setShadowIntensity(float shadowIntensity) {
+    final public void setShadowIntensity(float shadowIntensity) {
         this.shadowIntensity = shadowIntensity;
         postshadowMat.setFloat("ShadowIntensity", shadowIntensity);
     }

+ 27 - 30
engine/src/test/jme3test/light/TestTransparentShadow.java

@@ -42,10 +42,9 @@ import com.jme3.math.*;
 import com.jme3.renderer.queue.RenderQueue.Bucket;
 import com.jme3.renderer.queue.RenderQueue.ShadowMode;
 import com.jme3.scene.Geometry;
-import com.jme3.scene.SceneGraphVisitorAdapter;
 import com.jme3.scene.Spatial;
-import com.jme3.scene.control.LodControl;
 import com.jme3.scene.shape.Quad;
+import com.jme3.scene.shape.Sphere;
 import com.jme3.shadow.PssmShadowRenderer;
 import com.jme3.shadow.PssmShadowRenderer.CompareMode;
 import com.jme3.shadow.PssmShadowRenderer.FilterMode;
@@ -59,15 +58,16 @@ public class TestTransparentShadow extends SimpleApplication {
 
     public void simpleInitApp() {
 
-        cam.setLocation(new Vector3f(2.0606942f, 3.20342f, 6.7860126f));
-        cam.setRotation(new Quaternion(-0.017481906f, 0.98241085f, -0.12393151f, -0.13857932f));
+        cam.setLocation(new Vector3f(5.700248f, 6.161693f, 5.1404157f));
+        cam.setRotation(new Quaternion(-0.09441641f, 0.8993388f, -0.24089815f, -0.35248178f));
 
         viewPort.setBackgroundColor(ColorRGBA.DarkGray);
 
         Quad q = new Quad(20, 20);
-        q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(5));
+        q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(10));
         Geometry geom = new Geometry("floor", q);
         Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+        mat.setFloat("Shininess", 0);
         geom.setMaterial(mat);
         
         geom.rotate(-FastMath.HALF_PI, 0, 0);
@@ -76,34 +76,21 @@ public class TestTransparentShadow extends SimpleApplication {
         rootNode.attachChild(geom);
 
         // create the geometry and attach it
-        Spatial teaGeom = assetManager.loadModel("Models/Tree/Tree.mesh.j3o");
-        teaGeom.setQueueBucket(Bucket.Transparent);
-        teaGeom.setShadowMode(ShadowMode.Cast);
+        Spatial tree = assetManager.loadModel("Models/Tree/Tree.mesh.j3o");
+        tree.setQueueBucket(Bucket.Transparent);
+        tree.setShadowMode(ShadowMode.CastAndReceive);
 
-        teaGeom.depthFirstTraversal(new SceneGraphVisitorAdapter(){
-            @Override
-             public void visit(Geometry geom) {
-                 LodControl lodCtrl = new LodControl();
-                 lodCtrl.setTrisPerPixel(0.25f);
-                 geom.addControl(lodCtrl);
-             }
-        });
-        
+           
         AmbientLight al = new AmbientLight();
-        al.setColor(ColorRGBA.White.mult(2));
+        al.setColor(ColorRGBA.White.mult(0.7f));
         rootNode.addLight(al);
 
         DirectionalLight dl1 = new DirectionalLight();
-        dl1.setDirection(new Vector3f(1, -1, 1).normalizeLocal());
-        dl1.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
+        dl1.setDirection(new Vector3f(0, -1, 0.5f).normalizeLocal());
+        dl1.setColor(ColorRGBA.White.mult(1.5f));
         rootNode.addLight(dl1);
 
-        DirectionalLight dl = new DirectionalLight();
-        dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
-        dl.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
-        rootNode.addLight(dl);
-
-        rootNode.attachChild(teaGeom);    
+        rootNode.attachChild(tree);    
         
         /** Uses Texture from jme3-test-data library! */
         ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
@@ -125,16 +112,26 @@ public class TestTransparentShadow extends SimpleApplication {
         fire.setLocalTranslation(1.0f, 0, 1.0f);
         fire.setLocalScale(0.3f);
         fire.setQueueBucket(Bucket.Translucent);
-        rootNode.attachChild(fire);
+      //  rootNode.attachChild(fire);
 
         
+        Material mat2 = assetManager.loadMaterial("Common/Materials/RedColor.j3m");  
+ 
+
+        Geometry ball = new Geometry("sphere", new Sphere(16, 16, 0.5f));
+        ball.setMaterial(mat2);
+        ball.setShadowMode(ShadowMode.CastAndReceive);
+        rootNode.attachChild(ball);
+        ball.setLocalTranslation(-1.0f, 1.5f, 1.0f);
+         
+         
         PssmShadowRenderer pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 1);
-        pssmRenderer.setDirection(new Vector3f(0.01f, -1f, 0.01f).normalizeLocal());
+        pssmRenderer.setDirection(dl1.getDirection());
         pssmRenderer.setLambda(0.55f);
-        pssmRenderer.setShadowIntensity(0.6f);
+        pssmRenderer.setShadowIntensity(0.8f);
         pssmRenderer.setCompareMode(CompareMode.Software);
         pssmRenderer.setFilterMode(FilterMode.PCF4);
-        pssmRenderer.displayDebug();
+        //pssmRenderer.displayDebug();
         viewPort.addProcessor(pssmRenderer);
     }
 }