Selaa lähdekoodia

Lighting and Shadows (PSSM only) :
- re introduced the alphaDiscardThreshold as explained in prvious commit. It's not binded to the AlphaTestFallOff fixedfunc binding
- Added a small poly offset to post shadow technique, this greatly help in fixing shadow acne.
- Added Poisson disc sampling PCF Filtering for shadows
- Properly passed the shadow map size as a define in the shaders and remove the hardcoded value
- Pssm15 don't use the textureSize function anymore and use the same shadow map size define ( this increased performance quite a bit)
- Optimized the shaders code a bit
- Better PSSM test

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

rem..om 13 vuotta sitten
vanhempi
commit
384f4ac1c2

+ 22 - 9
engine/src/core-data/Common/MatDefs/Light/Lighting.j3md

@@ -15,6 +15,9 @@ MaterialDef Phong Lighting {
         // Output alpha from the diffuse map
         Boolean UseAlpha
 
+        // Apha threshold for fragment discarding
+        Float AlphaDiscardThreshold (AlphaTestFallOff)
+
         // Normal map is in BC5/ATI2n/LATC/3Dc compression format
         Boolean LATC
 
@@ -115,6 +118,7 @@ MaterialDef Phong Lighting {
         Matrix4 LightViewProjectionMatrix3
 
         Float PCFEdge
+        Float ShadowMapSize
     }
 
     Technique {
@@ -157,7 +161,7 @@ MaterialDef Phong Lighting {
             SEPARATE_TEXCOORD : SeparateTexCoord
 
             USE_REFLECTION : EnvMap
-            SPHERE_MAP : EnvMapAsSphereMap            
+            SPHERE_MAP : SphereMap            
         }
     }
 
@@ -171,8 +175,9 @@ MaterialDef Phong Lighting {
             WorldViewMatrix
         }
 
-        Defines {           
-            DIFFUSEMAP : DiffuseMap
+        Defines {
+            COLOR_MAP : ColorMap
+            DISCARD_ALPHA : AlphaDiscardThreshold
         }
 
         RenderState {
@@ -199,11 +204,15 @@ MaterialDef Phong Lighting {
             HARDWARE_SHADOWS : HardwareShadows
             FILTER_MODE : FilterMode
             PCFEDGE : PCFEdge
-            DIFFUSEMAP : DiffuseMap         
+            DISCARD_ALPHA : AlphaDiscardThreshold
+            COLOR_MAP : ColorMap
+            SHADOWMAP_SIZE : ShadowMapSize
         }
 
-        RenderState {
-            Blend Alpha
+        ForcedRenderState {
+            Blend Modulate
+            DepthWrite Off                 
+            PolyOffset -0.1 0
         }
     }
 
@@ -220,11 +229,15 @@ MaterialDef Phong Lighting {
             HARDWARE_SHADOWS : HardwareShadows
             FILTER_MODE : FilterMode
             PCFEDGE : PCFEdge
-            DIFFUSEMAP : DiffuseMap     
+            DISCARD_ALPHA : AlphaDiscardThreshold
+            COLOR_MAP : ColorMap
+            SHADOWMAP_SIZE : ShadowMapSize
         }
 
-        RenderState {
-            Blend Alpha
+        ForcedRenderState {
+            Blend Modulate
+            DepthWrite Off   
+            PolyOffset -0.1 0  
         }
     }
 

+ 2 - 0
engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md

@@ -20,6 +20,8 @@ MaterialDef Post Shadow {
 
         RenderState {
             Blend Modulate
+            DepthWrite Off    
+            PolyOffset -0.1 0
         }
     }
 

+ 89 - 23
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag

@@ -23,6 +23,9 @@
     #define GETSHADOW Shadow_DoPCF
     #define KERNEL 4.0
 #elif FILTER_MODE == 4
+    #define GETSHADOW Shadow_DoPCFPoisson
+    #define KERNEL 4
+#elif FILTER_MODE == 5
     #define GETSHADOW Shadow_DoPCF
     #define KERNEL 8.0
 #endif
@@ -44,9 +47,9 @@ varying vec4 projCoord3;
 
 varying float shadowPosition;
 
-const float texSize = 1024.0;
-const float pixSize = 1.0 / texSize;
-const vec2 pixSize2 = vec2(pixSize);
+
+const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
+float scale = 1.0;
 
 float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, in vec4 projCoord, in vec2 offset){
     vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2, projCoord.zw);
@@ -90,7 +93,7 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
     gather.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0));
     gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 1.0));
 
-    vec2 f = fract( projCoord.xy * texSize );
+    vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
     vec2 mx = mix( gather.xz, gather.yw, f.x );
     return mix( mx.x, mx.y, f.y );
 }
@@ -114,39 +117,102 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
     return shadow;
 }
 
-#ifdef COLOR_MAP
-    uniform sampler2D m_ColorMap;
-    varying vec2 texCoord;
-#endif    
-#ifdef DIFFUSEMAP
-    uniform sampler2D m_DiffuseMap;
+
+//12 tap poisson disk
+const vec2 poissonDisk[12] =vec2[12]( vec2(-0.1711046, -0.425016),
+ vec2(-0.7829809, 0.2162201),
+ vec2(-0.2380269, -0.8835521),
+ vec2(0.4198045, 0.1687819),
+ vec2(-0.684418, -0.3186957),
+ vec2(0.6026866, -0.2587841),
+ vec2(-0.2412762, 0.3913516),
+ vec2(0.4720655, -0.7664126),
+ vec2(0.9571564, 0.2680693),
+ vec2(-0.5238616, 0.802707),
+ vec2(0.5653144, 0.60262),
+ vec2(0.0123658, 0.8627419));
+
+float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){   
+    float shadow = 0.0;
+    float border = Shadow_BorderCheck(projCoord.xy);
+    if (border > 0.0)
+        return 1.0;
+
+    vec2 texelSize = vec2( 4.0 * PCFEDGE * scale);        
+    
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[0] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[1] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[2] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[3] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[4] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[5] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[6] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[7] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[8] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[9] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[10] * texelSize);
+     shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk[11] * texelSize);
+
+    shadow = shadow * 0.08333333333;//this is divided by 12
+    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 alpha =1.0;
-    
-    #ifdef COLOR_MAP
-        alpha = texture2D(m_ColorMap,texCoord).a;
+    #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
-    #ifdef DIFFUSEMAP
-        alpha = texture2D(m_DiffuseMap,texCoord).a;
-    #endif       
-    
    
+
     vec4 shadowPerSplit = vec4(0.0);
-    shadowPerSplit.x = GETSHADOW(m_ShadowMap0, projCoord0);
+float shadow;
+//shadowPosition
+    if(shadowPosition < m_Splits.x){
+        shadow= GETSHADOW(m_ShadowMap0, projCoord0);
+    }else if( shadowPosition <  m_Splits.y){
+        scale = 0.5;
+        shadow = GETSHADOW(m_ShadowMap1, projCoord1);
+    }else if( shadowPosition <  m_Splits.z){
+        scale = 0.25;
+        shadow= GETSHADOW(m_ShadowMap2, projCoord2);
+    }else if( shadowPosition <  m_Splits.w){
+        scale = 0.125;
+        shadow= GETSHADOW(m_ShadowMap3, projCoord3);
+    }
+/*    
+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(0.0, 0.0, 0.0, min(1.0 - shadow,alpha));
+  
+
+  gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
+
 }
 

+ 10 - 2
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md

@@ -18,6 +18,8 @@ MaterialDef Post Shadow {
         Matrix4 LightViewProjectionMatrix3
 
         Float PCFEdge
+
+        Float ShadowMapSize
     }
 
     Technique {
@@ -33,10 +35,13 @@ MaterialDef Post Shadow {
             HARDWARE_SHADOWS : HardwareShadows
             FILTER_MODE : FilterMode
             PCFEDGE : PCFEdge
+            SHADOWMAP_SIZE : ShadowMapSize
         }
 
         RenderState {
-            Blend Alpha
+            Blend Modulate
+            DepthWrite Off   
+            PolyOffset -0.1 0             
         }
     }
 
@@ -53,10 +58,13 @@ MaterialDef Post Shadow {
             HARDWARE_SHADOWS : HardwareShadows
             FILTER_MODE : FilterMode
             PCFEDGE : PCFEdge
+            SHADOWMAP_SIZE : ShadowMapSize
         }
 
         RenderState {
-            Blend Alpha
+            Blend Modulate
+            DepthWrite Off    
+            PolyOffset -0.1 0
         }
     }
 

+ 3 - 2
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert

@@ -16,7 +16,8 @@ varying float shadowPosition;
 varying vec2 texCoord;
 
 attribute vec3 inPosition;
-#if defined(DIFFUSEMAP) || defined(COLOR_MAP)
+
+#ifdef DISCARD_ALPHA
     attribute vec2 inTexCoord;
 #endif
 
@@ -33,7 +34,7 @@ void main(){
     // get the vertex in world space
     vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
 
-    #if defined(DIFFUSEMAP) || defined(COLOR_MAP)
+    #ifdef DISCARD_ALPHA
        texCoord = inTexCoord;
     #endif
     // populate the light view matrices array and convert vertex to light viewProj space

+ 79 - 24
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag

@@ -32,6 +32,9 @@
     #define GETSHADOW Shadow_DoPCF
     #define KERNEL 4
 #elif FILTER_MODE == 4
+    #define GETSHADOW Shadow_DoPCFPoisson
+    #define KERNEL 4
+#elif FILTER_MODE == 5
     #define GETSHADOW Shadow_DoPCF
     #define KERNEL 8
 #endif
@@ -51,6 +54,8 @@ in vec4 projCoord1;
 in vec4 projCoord2;
 in vec4 projCoord3;
 in float shadowPosition;
+const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
+float scale = 1.0;
 
 float Shadow_BorderCheck(in vec2 coord){
     // Fastest, "hack" method (uses 4-5 instructions)
@@ -64,9 +69,8 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
     if (border > 0.0)
         return 1.0;
 
-    ivec2 texSize = textureSize(tex, 0);
-    vec2 pixSize = 1.0 / vec2(texSize);
-
+    vec2 pixSize = pixSize2 * scale;
+    
     float shadow = 0.0;
     ivec2 o = ivec2(mod(floor(gl_FragCoord.xy), 2.0));
     shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, 1.5)+o), projCoord.zw));
@@ -81,8 +85,7 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
     float border = Shadow_BorderCheck(projCoord.xy);
     if (border > 0.0)
         return 1.0;
-
-    ivec2 texSize = textureSize(tex, 0);
+  
     #ifdef GL_ARB_gpu_shader5
         vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0);
         vec4 gather = SHADOWGATHER(tex, coord);
@@ -94,14 +97,14 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
         gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
    #endif
 
-   vec2 f = fract( projCoord.xy * texSize );
+   vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
    vec2 mx = mix( gather.xz, gather.yw, f.x );
    return mix( mx.x, mx.y, f.y );
 }
 
 float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
-    float pixSize = 1.0 / textureSize(tex,0).x;
-
+    
+    vec2 pixSize = pixSize2 * scale;   
     float shadow = 0.0;
     float border = Shadow_BorderCheck(projCoord.xy);
     if (border > 0.0)
@@ -121,38 +124,90 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
 }
 
 
-#ifdef COLOR_MAP
-    uniform sampler2D m_ColorMap;
-    varying vec2 texCoord;
-#endif    
-#ifdef DIFFUSEMAP  
-    uniform sampler2D m_DiffuseMap;
-    varying vec2 texCoord;
-#endif
- 
-void main(){
+//12 tap poisson disk
+const vec2 poissonDisk[12] =vec2[12]( vec2(-0.1711046, -0.425016),
+ vec2(-0.7829809, 0.2162201),
+ vec2(-0.2380269, -0.8835521),
+ vec2(0.4198045, 0.1687819),
+ vec2(-0.684418, -0.3186957),
+ vec2(0.6026866, -0.2587841),
+ vec2(-0.2412762, 0.3913516),
+ vec2(0.4720655, -0.7664126),
+ vec2(0.9571564, 0.2680693),
+ vec2(-0.5238616, 0.802707),
+ vec2(0.5653144, 0.60262),
+ vec2(0.0123658, 0.8627419));
+
+
+float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
+
     float shadow = 0.0;
+    float border = Shadow_BorderCheck(projCoord.xy);
+    if (border > 0.0)
+        return 1.0;
 
-    float alpha = 1.0;
+    //failed attempt to rotate the poisson disk to add jitter
+    //vec2 jitterFactor = vec2(sin(projCoord.x),cos(projCoord.x));// * 2.0f - 1.0f;
+     
+    vec2 texelSize = pixSize2 * 4.0 * PCFEDGE * scale;        
     
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[0] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[1] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[2] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[3] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[4] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[5] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[6] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[7] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[8] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[9] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[10] * texelSize, projCoord.zw));
+    shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk[11] * texelSize, projCoord.zw));
+
+    shadow = shadow * 0.08333333333;//this is divided by 12
+    return shadow;
+}
+
+#ifdef DISCARD_ALPHA
     #ifdef COLOR_MAP
-          alpha = texture2D(m_ColorMap,texCoord).a;
+        uniform sampler2D m_ColorMap;
+    #else    
+        uniform sampler2D m_DiffuseMap;
     #endif
-    #ifdef DIFFUSEMAP   
-          alpha = texture2D(m_DiffuseMap,texCoord).a;
+    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){
+        scale = 0.5;
         shadow = GETSHADOW(m_ShadowMap1, projCoord1);
     }else if( shadowPosition <  m_Splits.z){
+        scale = 0.25;
         shadow = GETSHADOW(m_ShadowMap2, projCoord2);
     }else if( shadowPosition <  m_Splits.w){
+        scale = 0.125;
         shadow = GETSHADOW(m_ShadowMap3, projCoord3);
     }
     
     shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
-    outFragColor =  vec4(0.0, 0.0, 0.0, min(1.0 - shadow,alpha));
+    outFragColor =  vec4(shadow, shadow, shadow, 1.0);
+
 }
 

+ 19 - 16
engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag

@@ -1,24 +1,27 @@
 varying vec2 texCoord;
 
-
-#ifdef COLOR_MAP
-  uniform sampler2D m_ColorMap;
-#endif 
-#ifdef DIFFUSEMAP
-  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(){
-    float a = 1.0;
-    
-    #ifdef COLOR_MAP
-        a = texture2D(m_ColorMap, texCoord).a;
-    #endif    
-    #ifdef DIFFUSEMAP
-        a = texture2D(m_DiffuseMap, texCoord).a;
-    #endif
+   #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(a);
+   gl_FragColor = vec4(1.0);
 }

+ 6 - 4
engine/src/core-effects/Common/MatDefs/SSAO/normal.frag

@@ -4,15 +4,17 @@ varying vec2 texCoord;
 
 #ifdef DIFFUSEMAP_ALPHA
     uniform sampler2D m_DiffuseMap;
+    uniform float m_AlphaDiscardThreshold;
 #endif
 
 void main(void)
 {
-    float alpha= 1.0;
+
     #ifdef DIFFUSEMAP_ALPHA
-        alpha=texture2D(m_DiffuseMap,texCoord).a;
+        if(texture2D(m_DiffuseMap,texCoord).a<m_AlphaDiscardThreshold){
+            discard;
+        }
     #endif
-    gl_FragColor = vec4(normal.xy* 0.5 + 0.5,-normal.z* 0.5 + 0.5, alpha);
+    gl_FragColor = vec4(normal.xy* 0.5 + 0.5,-normal.z* 0.5 + 0.5, 1.0);
 
 }
-

+ 24 - 10
engine/src/core/com/jme3/shadow/PssmShadowRenderer.java

@@ -31,6 +31,7 @@ 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;
@@ -47,6 +48,7 @@ import com.jme3.renderer.queue.RenderQueue.ShadowMode;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.debug.WireFrustum;
+import com.jme3.shader.VarType;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Image.Format;
 import com.jme3.texture.Texture.MagFilter;
@@ -93,11 +95,17 @@ public class PssmShadowRenderer implements SceneProcessor {
          * at the cost of performance
          */
         PCF4,
+        /**
+         * 8x8 percentage-closer  filtering is used. Shadows will be smoother
+         * at the cost of performance
+         */
+        PCFPOISSON,        
         /**
          * 8x8 percentage-closer  filtering is used. Shadows will be smoother
          * at the cost of performance
          */
         PCF8
+        
     }
 
     /**
@@ -116,6 +124,7 @@ public class PssmShadowRenderer implements SceneProcessor {
         Hardware;
     }
     private int nbSplits = 3;
+    private float shadowMapSize;
     private float lambda = 0.65f;
     private float shadowIntensity = 0.7f;
     private float zFarOverride = 0;
@@ -172,6 +181,7 @@ public class PssmShadowRenderer implements SceneProcessor {
         assetManager = manager;
         nbSplits = Math.max(Math.min(nbSplits, 4), 1);
         this.nbSplits = nbSplits;
+        shadowMapSize = size;
 
         shadowFB = new FrameBuffer[nbSplits];
         shadowMaps = new Texture2D[nbSplits];
@@ -185,7 +195,8 @@ public class PssmShadowRenderer implements SceneProcessor {
 
         preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
         this.postshadowMat = postShadowMat;
-
+        postshadowMat.setFloat("ShadowMapSize", size);       
+        
         for (int i = 0; i < nbSplits; i++) {
             lightViewProjectionsMatrices[i] = new Matrix4f();
             shadowFB[i] = new FrameBuffer(size, size, 1);
@@ -213,7 +224,7 @@ public class PssmShadowRenderer implements SceneProcessor {
         for (int i = 0; i < points.length; i++) {
             points[i] = new Vector3f();
         }
-       
+
     }
 
     /**
@@ -315,7 +326,7 @@ public class PssmShadowRenderer implements SceneProcessor {
         //checking for caps to chosse the appropriate post material technique
         if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) {
             postTechniqueName = "PostShadow15";
-        }else{
+        } else {
             postTechniqueName = "PostShadow";
         }
     }
@@ -423,7 +434,7 @@ public class PssmShadowRenderer implements SceneProcessor {
         renderManager.setCamera(cam, true);
         int h = cam.getHeight();
         for (int i = 0; i < dispPic.length; i++) {
-            dispPic[i].setPosition(64 * (i + 1) + 128 * i, h / 20f);
+            dispPic[i].setPosition((128 * i) +(150 + 64 * (i + 1) ), h / 20f);
             dispPic[i].setWidth(128);
             dispPic[i].setHeight(128);
             dispPic[i].updateGeometricState();
@@ -448,16 +459,16 @@ public class PssmShadowRenderer implements SceneProcessor {
             if (needsfallBackMaterial) {
                 renderManager.setForcedMaterial(postshadowMat);
             }
-            
-            //forcing the post shadow technique
+
+            //forcing the post shadow technique and render state
             renderManager.setForcedTechnique(postTechniqueName);
-            
+
             //rendering the post shadow pass
             viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues);
 
             //resetting renderManager settings
             renderManager.setForcedTechnique(null);
-            renderManager.setForcedMaterial(null);       
+            renderManager.setForcedMaterial(null);
             renderManager.setCamera(cam, false);
 
         }
@@ -469,7 +480,7 @@ 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();
@@ -485,7 +496,10 @@ public class PssmShadowRenderer implements SceneProcessor {
                 mat.setInt("FilterMode", filterMode.ordinal());
                 mat.setFloat("PCFEdge", edgesThickness);
                 mat.setFloat("ShadowIntensity", shadowIntensity);
-            } else {                
+                if(mat.getParam("ShadowMapSize") == null){
+                    mat.setFloat("ShadowMapSize", shadowMapSize);
+                }
+            } else {
                 needsfallBackMaterial = true;
             }
         }

+ 134 - 39
engine/src/test/jme3test/light/TestPssmShadow.java

@@ -29,98 +29,143 @@
  * 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.light;
 
 import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
 import com.jme3.input.KeyInput;
 import com.jme3.input.controls.ActionListener;
 import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+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.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.queue.RenderQueue.ShadowMode;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Spatial;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.control.Control;
 import com.jme3.scene.shape.Box;
 import com.jme3.scene.shape.Sphere;
 import com.jme3.shadow.PssmShadowRenderer;
 import com.jme3.shadow.PssmShadowRenderer.CompareMode;
 import com.jme3.shadow.PssmShadowRenderer.FilterMode;
-import java.util.Random;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import com.jme3.util.TangentBinormalGenerator;
 
 public class TestPssmShadow extends SimpleApplication implements ActionListener {
 
-    private Spatial teapot;
+    private Spatial[] obj;
+    private Material[] mat;
     private boolean renderShadows = true;
     private boolean hardwareShadows = false;
     private PssmShadowRenderer pssmRenderer;
+    private Geometry ground;
+    private Material matGroundU; 
+    private Material matGroundL; 
+    
 
-    public static void main(String[] args){
+    public static void main(String[] args) {
         TestPssmShadow app = new TestPssmShadow();
         app.start();
     }
 
-    public void loadScene(){
-        Material mat = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
-        Material matSoil = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md");
-        matSoil.setColor("Color", ColorRGBA.Cyan);
+    public void loadScene() {
+        obj = new Spatial[2];
+        mat = new Material[2];
+        mat[0] = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
+        mat[1] = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+        mat[1].setBoolean("UseMaterialColors", true);
+        mat[1].setColor("Ambient", ColorRGBA.White.mult(0.5f));
+        mat[1].setColor("Diffuse", ColorRGBA.White.clone());
 
-        teapot = new Geometry("sphere", new Sphere(30, 30, 2));
-//        teapot = new Geometry("cube", new Box(1.0f, 1.0f, 1.0f));
-//        teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
-        teapot.setLocalTranslation(0,0,10);
 
-        teapot.setMaterial(mat);
-        teapot.setShadowMode(ShadowMode.CastAndReceive);
-        rootNode.attachChild(teapot);
+        obj[0] = new Geometry("sphere", new Sphere(30, 30, 2));
+        obj[0].setShadowMode(ShadowMode.CastAndReceive);
+        obj[1] = new Geometry("cube", new Box(1.0f, 1.0f, 1.0f));
+        obj[1].setShadowMode(ShadowMode.CastAndReceive);
+        TangentBinormalGenerator.generate(obj[1]);
+        TangentBinormalGenerator.generate(obj[0]);
 
-        long seed = 1294719330150L; //System.currentTimeMillis();
-        Random random = new Random(seed);
-        System.out.println(seed);
 
-        for (int i = 0; i < 30; i++) {
-            Spatial t = teapot.clone(false);
+        for (int i = 0; i < 60; i++) {
+            Spatial t = obj[FastMath.nextRandomInt(0, obj.length - 1)].clone(false);
+            t.setLocalScale(FastMath.nextRandomFloat() * 10f);
+            t.setMaterial(mat[FastMath.nextRandomInt(0, mat.length - 1)]);
             rootNode.attachChild(t);
-            teapot.setLocalTranslation((float) random.nextFloat() * 3, (float) random.nextFloat() * 3, (i + 2));
+            t.setLocalTranslation(FastMath.nextRandomFloat() * 200f, FastMath.nextRandomFloat() * 30f + 20, 30f * (i + 2f));
         }
 
-        Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -13, 550), 800, 10, 700));
-        soil.setMaterial(matSoil);
-        soil.setShadowMode(ShadowMode.Receive);
-        rootNode.attachChild(soil);
+        Box b = new Box(new Vector3f(0, 10, 550), 1000, 2, 1000);
+        b.scaleTextureCoordinates(new Vector2f(10, 10));
+        ground = new Geometry("soil", b);
+        matGroundU = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        matGroundU.setColor("Color", ColorRGBA.Green);
+       
+        
+        matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+        Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+        grass.setWrap(WrapMode.Repeat);
+        matGroundL.setTexture("DiffuseMap", grass);
+        
+        ground.setMaterial(matGroundL);
+       
+        ground.setShadowMode(ShadowMode.CastAndReceive);
+        rootNode.attachChild(ground);
 
-        for (int i = 0; i < 30; i++) {
-            Spatial t = teapot.clone(false);
-            t.setLocalScale(10.0f);
-            rootNode.attachChild(t);
-            teapot.setLocalTranslation((float) random.nextFloat() * 300, (float) random.nextFloat() * 30, 30 * (i + 2));
-        }
+        DirectionalLight l = new DirectionalLight();
+        l.setDirection(new Vector3f(-1, -1, -1));
+        rootNode.addLight(l);
+
+        AmbientLight al = new AmbientLight();
+        al.setColor(ColorRGBA.White.mult(0.5f));
+        rootNode.addLight(al);
+
+        Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false);
+        sky.setLocalScale(350);
+
+        rootNode.attachChild(sky);
     }
 
     @Override
     public void simpleInitApp() {
         // put the camera in a bad position
-        cam.setLocation(new Vector3f(41.59757f, 34.38738f, 11.528807f));
-        cam.setRotation(new Quaternion(0.2905285f, 0.3816416f, -0.12772122f, 0.86811876f));
+        cam.setLocation(new Vector3f(65.25412f, 44.38738f, 9.087874f));
+        cam.setRotation(new Quaternion(0.078139365f, 0.050241485f, -0.003942559f, 0.9956679f));
+
         flyCam.setMoveSpeed(100);
 
         loadScene();
-           
+
         pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 3);
         pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
         pssmRenderer.setLambda(0.55f);
         pssmRenderer.setShadowIntensity(0.6f);
         pssmRenderer.setCompareMode(CompareMode.Software);
-        pssmRenderer.setFilterMode(FilterMode.Bilinear);
+        pssmRenderer.setFilterMode(FilterMode.Dither);
         pssmRenderer.displayDebug();
         viewPort.addProcessor(pssmRenderer);
         initInputs();
     }
+    BitmapText infoText;
+
+    private void initInputs() {
+        /** Write text on the screen (HUD) */
+        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        infoText = new BitmapText(guiFont, false);
+        infoText.setSize(guiFont.getCharSet().getRenderedSize());
+
 
-      private void initInputs() {
         inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+        inputManager.addMapping("changeFiltering", new KeyTrigger(KeyInput.KEY_F));
         inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T));
         inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G));
         inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y));
@@ -128,9 +173,46 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
         inputManager.addMapping("lambdaUp", new KeyTrigger(KeyInput.KEY_U));
         inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J));
         inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN));
-        inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp","ShadowDown","ThicknessUp","ThicknessDown");
+        inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M));
+        inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown","changeFiltering","switchGroundMat");
     }
 
+    private void print(String str) {
+        infoText.setText(str);
+        infoText.setLocalTranslation(cam.getWidth() * 0.5f - infoText.getLineWidth() * 0.5f, infoText.getLineHeight(), 0);
+        guiNode.attachChild(infoText);
+        infoText.removeControl(ctrl);
+        infoText.addControl(ctrl);
+    }
+    AbstractControl ctrl = new AbstractControl() {
+
+        float time;
+
+        @Override
+        protected void controlUpdate(float tpf) {
+            time += tpf;
+            if (time > 3) {
+                spatial.removeFromParent();
+                spatial.removeControl(this);
+            }
+        }
+
+        @Override
+        public void setSpatial(Spatial spatial) {
+            super.setSpatial(spatial);
+            time = 0;
+        }
+
+        @Override
+        protected void controlRender(RenderManager rm, ViewPort vp) {
+        }
+
+        public Control cloneForSpatial(Spatial spatial) {
+            return null;
+        }
+    };
+    int filteringIndex = 2;
+
     public void onAction(String name, boolean keyPressed, float tpf) {
         if (name.equals("toggle") && keyPressed) {
             if (renderShadows) {
@@ -146,6 +228,13 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
             System.out.println("HW Shadows: " + hardwareShadows);
         }
 
+        if (name.equals("changeFiltering") && keyPressed) {
+            filteringIndex = (filteringIndex + 1) % FilterMode.values().length;
+            FilterMode m = FilterMode.values()[filteringIndex];
+            pssmRenderer.setFilterMode(m);
+            print("Filter mode : " + m.toString());
+        }
+
         if (name.equals("lambdaUp") && keyPressed) {
             pssmRenderer.setLambda(pssmRenderer.getLambda() + 0.01f);
             System.out.println("Lambda : " + pssmRenderer.getLambda());
@@ -170,7 +259,13 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
             pssmRenderer.setEdgesThickness(pssmRenderer.getEdgesThickness() - 1);
             System.out.println("Shadow thickness : " + pssmRenderer.getEdgesThickness());
         }
+        if (name.equals("switchGroundMat") && keyPressed) {
+            if(ground.getMaterial() == matGroundL){
+                ground.setMaterial(matGroundU);
+            }else{
+                ground.setMaterial(matGroundL);
+            }            
+        }
+        
     }
-
-
 }

+ 2 - 2
engine/src/test/jme3test/light/TestTransparentShadow.java

@@ -72,7 +72,7 @@ public class TestTransparentShadow extends SimpleApplication {
         
         geom.rotate(-FastMath.HALF_PI, 0, 0);
         geom.center();
-        geom.setShadowMode(ShadowMode.Receive);
+        geom.setShadowMode(ShadowMode.CastAndReceive);
         rootNode.attachChild(geom);
 
         // create the geometry and attach it
@@ -132,6 +132,6 @@ public class TestTransparentShadow extends SimpleApplication {
         pssmRenderer.setCompareMode(CompareMode.Software);
         pssmRenderer.setFilterMode(FilterMode.PCF4);
         //pssmRenderer.displayDebug();
-        viewPort.addProcessor(pssmRenderer);
+         viewPort.addProcessor(pssmRenderer);
     }
 }

+ 3 - 1
engine/src/test/jme3test/post/TestTransparentSSAO.java

@@ -12,6 +12,7 @@ import com.jme3.renderer.queue.RenderQueue.ShadowMode;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.shape.Quad;
+import com.jme3.util.TangentBinormalGenerator;
 
 public class TestTransparentSSAO extends SimpleApplication {
 
@@ -39,6 +40,7 @@ public class TestTransparentSSAO extends SimpleApplication {
         geom.rotate(-FastMath.HALF_PI, 0, 0);
         geom.center();
         geom.setShadowMode(ShadowMode.Receive);
+        TangentBinormalGenerator.generate(geom);
         rootNode.attachChild(geom);
 
         // create the geometry and attach it
@@ -64,7 +66,7 @@ public class TestTransparentSSAO extends SimpleApplication {
 
         FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
 
-        SSAOFilter ssao = new SSAOFilter(0.49997783f, 42.598858f, 35.999966f, 0.39299846f);
+        SSAOFilter ssao = new SSAOFilter();//0.49997783f, 42.598858f, 35.999966f, 0.39299846f
         fpp.addFilter(ssao);
 
         SSAOUI ui = new SSAOUI(inputManager, ssao);

+ 2 - 1
engine/test-data/Models/Tree/Leaves.j3m

@@ -4,7 +4,8 @@ Material Leaves : Common/MatDefs/Light/Lighting.j3md {
 
     MaterialParameters {
         DiffuseMap : Models/Tree/Leaves.png
-        UseAlpha : true        
+        UseAlpha : true
+        AlphaDiscardThreshold : 0.5
         UseMaterialColors : true
         Ambient : .5 .5 .5 .5
         Diffuse : 0.7 0.7 0.7 1