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
3.0
rem..om 12 years ago
parent 2915316e45
commit 384f4ac1c2
  1. 29
      engine/src/core-data/Common/MatDefs/Light/Lighting.j3md
  2. 2
      engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md
  3. 106
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag
  4. 12
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md
  5. 5
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert
  6. 93
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag
  7. 35
      engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag
  8. 10
      engine/src/core-effects/Common/MatDefs/SSAO/normal.frag
  9. 20
      engine/src/core/com/jme3/shadow/PssmShadowRenderer.java
  10. 171
      engine/src/test/jme3test/light/TestPssmShadow.java
  11. 4
      engine/src/test/jme3test/light/TestTransparentShadow.java
  12. 4
      engine/src/test/jme3test/post/TestTransparentSSAO.java
  13. 1
      engine/test-data/Models/Tree/Leaves.j3m

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

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

@ -23,6 +23,9 @@
#define GETSHADOW Shadow_DoPCF #define GETSHADOW Shadow_DoPCF
#define KERNEL 4.0 #define KERNEL 4.0
#elif FILTER_MODE == 4 #elif FILTER_MODE == 4
#define GETSHADOW Shadow_DoPCFPoisson
#define KERNEL 4
#elif FILTER_MODE == 5
#define GETSHADOW Shadow_DoPCF #define GETSHADOW Shadow_DoPCF
#define KERNEL 8.0 #define KERNEL 8.0
#endif #endif
@ -44,9 +47,9 @@ varying vec4 projCoord3;
varying float shadowPosition; varying float shadowPosition;
const float texSize = 1024.0;
const float pixSize = 1.0 / texSize; const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
const vec2 pixSize2 = vec2(pixSize); float scale = 1.0;
float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, in vec4 projCoord, in vec2 offset){ float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, in vec4 projCoord, in vec2 offset){
vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2, projCoord.zw); 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.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0));
gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.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 ); vec2 mx = mix( gather.xz, gather.yw, f.x );
return mix( mx.x, mx.y, f.y ); return mix( mx.x, mx.y, f.y );
} }
@ -114,39 +117,102 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
return shadow; return shadow;
} }
#ifdef COLOR_MAP
uniform sampler2D m_ColorMap; //12 tap poisson disk
varying vec2 texCoord; const vec2 poissonDisk[12] =vec2[12]( vec2(-0.1711046, -0.425016),
#endif vec2(-0.7829809, 0.2162201),
#ifdef DIFFUSEMAP vec2(-0.2380269, -0.8835521),
uniform sampler2D m_DiffuseMap; 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; varying vec2 texCoord;
#endif #endif
void main(){ void main(){
float alpha =1.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;
}
#ifdef COLOR_MAP
alpha = texture2D(m_ColorMap,texCoord).a;
#endif
#ifdef DIFFUSEMAP
alpha = texture2D(m_DiffuseMap,texCoord).a;
#endif #endif
vec4 shadowPerSplit = vec4(0.0); 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.y = GETSHADOW(m_ShadowMap1, projCoord1);
shadowPerSplit.z = GETSHADOW(m_ShadowMap2, projCoord2); shadowPerSplit.z = GETSHADOW(m_ShadowMap2, projCoord2);
shadowPerSplit.w = GETSHADOW(m_ShadowMap3, projCoord3); shadowPerSplit.w = GETSHADOW(m_ShadowMap3, projCoord3);
*/
/*
vec4 less = step( shadowPosition, m_Splits ); vec4 less = step( shadowPosition, m_Splits );
vec4 more = vec4(1.0) - step( shadowPosition, vec4(0.0, m_Splits.xyz) ); vec4 more = vec4(1.0) - step( shadowPosition, vec4(0.0, m_Splits.xyz) );
float shadow = dot(shadowPerSplit, less * more ); float shadow = dot(shadowPerSplit, less * more );
*/
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); 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);
} }

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

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

@ -32,6 +32,9 @@
#define GETSHADOW Shadow_DoPCF #define GETSHADOW Shadow_DoPCF
#define KERNEL 4 #define KERNEL 4
#elif FILTER_MODE == 4 #elif FILTER_MODE == 4
#define GETSHADOW Shadow_DoPCFPoisson
#define KERNEL 4
#elif FILTER_MODE == 5
#define GETSHADOW Shadow_DoPCF #define GETSHADOW Shadow_DoPCF
#define KERNEL 8 #define KERNEL 8
#endif #endif
@ -51,6 +54,8 @@ in vec4 projCoord1;
in vec4 projCoord2; in vec4 projCoord2;
in vec4 projCoord3; in vec4 projCoord3;
in float shadowPosition; in float shadowPosition;
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
float scale = 1.0;
float Shadow_BorderCheck(in vec2 coord){ float Shadow_BorderCheck(in vec2 coord){
// Fastest, "hack" method (uses 4-5 instructions) // Fastest, "hack" method (uses 4-5 instructions)
@ -64,8 +69,7 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
if (border > 0.0) if (border > 0.0)
return 1.0; return 1.0;
ivec2 texSize = textureSize(tex, 0); vec2 pixSize = pixSize2 * scale;
vec2 pixSize = 1.0 / vec2(texSize);
float shadow = 0.0; float shadow = 0.0;
ivec2 o = ivec2(mod(floor(gl_FragCoord.xy), 2.0)); ivec2 o = ivec2(mod(floor(gl_FragCoord.xy), 2.0));
@ -82,7 +86,6 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
if (border > 0.0) if (border > 0.0)
return 1.0; return 1.0;
ivec2 texSize = textureSize(tex, 0);
#ifdef GL_ARB_gpu_shader5 #ifdef GL_ARB_gpu_shader5
vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0); vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0);
vec4 gather = SHADOWGATHER(tex, coord); 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)); gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
#endif #endif
vec2 f = fract( projCoord.xy * texSize ); vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
vec2 mx = mix( gather.xz, gather.yw, f.x ); vec2 mx = mix( gather.xz, gather.yw, f.x );
return mix( mx.x, mx.y, f.y ); return mix( mx.x, mx.y, f.y );
} }
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ 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 shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy); float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0) if (border > 0.0)
@ -121,38 +124,90 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
} }
#ifdef COLOR_MAP //12 tap poisson disk
uniform sampler2D m_ColorMap; const vec2 poissonDisk[12] =vec2[12]( vec2(-0.1711046, -0.425016),
varying vec2 texCoord; vec2(-0.7829809, 0.2162201),
#endif vec2(-0.2380269, -0.8835521),
#ifdef DIFFUSEMAP vec2(0.4198045, 0.1687819),
uniform sampler2D m_DiffuseMap; 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;
//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
uniform sampler2D m_ColorMap;
#else
uniform sampler2D m_DiffuseMap;
#endif
uniform float m_AlphaDiscardThreshold;
varying vec2 texCoord; varying vec2 texCoord;
#endif #endif
void main(){ void main(){
float shadow = 0.0; float shadow = 0.0;
float alpha = 1.0;
#ifdef COLOR_MAP #ifdef DISCARD_ALPHA
alpha = texture2D(m_ColorMap,texCoord).a; #ifdef COLOR_MAP
#endif float alpha = texture2D(m_ColorMap,texCoord).a;
#ifdef DIFFUSEMAP #else
alpha = texture2D(m_DiffuseMap,texCoord).a; float alpha = texture2D(m_DiffuseMap,texCoord).a;
#endif #endif
if(alpha < m_AlphaDiscardThreshold){
discard;
}
#endif
if(shadowPosition < m_Splits.x){ if(shadowPosition < m_Splits.x){
shadow = GETSHADOW(m_ShadowMap0, projCoord0); shadow = GETSHADOW(m_ShadowMap0, projCoord0);
}else if( shadowPosition < m_Splits.y){ }else if( shadowPosition < m_Splits.y){
scale = 0.5;
shadow = GETSHADOW(m_ShadowMap1, projCoord1); shadow = GETSHADOW(m_ShadowMap1, projCoord1);
}else if( shadowPosition < m_Splits.z){ }else if( shadowPosition < m_Splits.z){
scale = 0.25;
shadow = GETSHADOW(m_ShadowMap2, projCoord2); shadow = GETSHADOW(m_ShadowMap2, projCoord2);
}else if( shadowPosition < m_Splits.w){ }else if( shadowPosition < m_Splits.w){
scale = 0.125;
shadow = GETSHADOW(m_ShadowMap3, projCoord3); shadow = GETSHADOW(m_ShadowMap3, projCoord3);
} }
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); 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);
} }

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

@ -4,15 +4,17 @@ varying vec2 texCoord;
#ifdef DIFFUSEMAP_ALPHA #ifdef DIFFUSEMAP_ALPHA
uniform sampler2D m_DiffuseMap; uniform sampler2D m_DiffuseMap;
uniform float m_AlphaDiscardThreshold;
#endif #endif
void main(void) void main(void)
{ {
float alpha= 1.0;
#ifdef DIFFUSEMAP_ALPHA #ifdef DIFFUSEMAP_ALPHA
alpha=texture2D(m_DiffuseMap,texCoord).a; if(texture2D(m_DiffuseMap,texCoord).a<m_AlphaDiscardThreshold){
discard;
}
#endif #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);
} }

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

@ -29,81 +29,118 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package jme3test.light; package jme3test.light;
import com.jme3.app.SimpleApplication; import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput; import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger; import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f; 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.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial; 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.Box;
import com.jme3.scene.shape.Sphere; import com.jme3.scene.shape.Sphere;
import com.jme3.shadow.PssmShadowRenderer; import com.jme3.shadow.PssmShadowRenderer;
import com.jme3.shadow.PssmShadowRenderer.CompareMode; import com.jme3.shadow.PssmShadowRenderer.CompareMode;
import com.jme3.shadow.PssmShadowRenderer.FilterMode; 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 { public class TestPssmShadow extends SimpleApplication implements ActionListener {
private Spatial teapot; private Spatial[] obj;
private Material[] mat;
private boolean renderShadows = true; private boolean renderShadows = true;
private boolean hardwareShadows = false; private boolean hardwareShadows = false;
private PssmShadowRenderer pssmRenderer; 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(); TestPssmShadow app = new TestPssmShadow();
app.start(); app.start();
} }
public void loadScene(){ public void loadScene() {
Material mat = assetManager.loadMaterial("Common/Materials/RedColor.j3m"); obj = new Spatial[2];
Material matSoil = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md"); mat = new Material[2];
matSoil.setColor("Color", ColorRGBA.Cyan); 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); obj[0] = new Geometry("sphere", new Sphere(30, 30, 2));
teapot.setShadowMode(ShadowMode.CastAndReceive); obj[0].setShadowMode(ShadowMode.CastAndReceive);
rootNode.attachChild(teapot); 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++) { for (int i = 0; i < 60; i++) {
Spatial t = teapot.clone(false); 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); 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)); Box b = new Box(new Vector3f(0, 10, 550), 1000, 2, 1000);
soil.setMaterial(matSoil); b.scaleTextureCoordinates(new Vector2f(10, 10));
soil.setShadowMode(ShadowMode.Receive); ground = new Geometry("soil", b);
rootNode.attachChild(soil); matGroundU = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
matGroundU.setColor("Color", ColorRGBA.Green);
for (int i = 0; i < 30; i++) {
Spatial t = teapot.clone(false); matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
t.setLocalScale(10.0f); Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
rootNode.attachChild(t); grass.setWrap(WrapMode.Repeat);
teapot.setLocalTranslation((float) random.nextFloat() * 300, (float) random.nextFloat() * 30, 30 * (i + 2)); matGroundL.setTexture("DiffuseMap", grass);
}
ground.setMaterial(matGroundL);
ground.setShadowMode(ShadowMode.CastAndReceive);
rootNode.attachChild(ground);
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 @Override
public void simpleInitApp() { public void simpleInitApp() {
// put the camera in a bad position // put the camera in a bad position
cam.setLocation(new Vector3f(41.59757f, 34.38738f, 11.528807f)); cam.setLocation(new Vector3f(65.25412f, 44.38738f, 9.087874f));
cam.setRotation(new Quaternion(0.2905285f, 0.3816416f, -0.12772122f, 0.86811876f)); cam.setRotation(new Quaternion(0.078139365f, 0.050241485f, -0.003942559f, 0.9956679f));
flyCam.setMoveSpeed(100); flyCam.setMoveSpeed(100);
loadScene(); loadScene();
@ -113,14 +150,22 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
pssmRenderer.setLambda(0.55f); pssmRenderer.setLambda(0.55f);
pssmRenderer.setShadowIntensity(0.6f); pssmRenderer.setShadowIntensity(0.6f);
pssmRenderer.setCompareMode(CompareMode.Software); pssmRenderer.setCompareMode(CompareMode.Software);
pssmRenderer.setFilterMode(FilterMode.Bilinear); pssmRenderer.setFilterMode(FilterMode.Dither);
pssmRenderer.displayDebug(); pssmRenderer.displayDebug();
viewPort.addProcessor(pssmRenderer); viewPort.addProcessor(pssmRenderer);
initInputs(); 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("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping("changeFiltering", new KeyTrigger(KeyInput.KEY_F));
inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T)); inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T));
inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G)); inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G));
inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y)); inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y));
@ -128,8 +173,45 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
inputManager.addMapping("lambdaUp", new KeyTrigger(KeyInput.KEY_U)); inputManager.addMapping("lambdaUp", new KeyTrigger(KeyInput.KEY_U));
inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J)); inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J));
inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN)); 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) { public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals("toggle") && keyPressed) { if (name.equals("toggle") && keyPressed) {
@ -146,6 +228,13 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
System.out.println("HW Shadows: " + hardwareShadows); 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) { if (name.equals("lambdaUp") && keyPressed) {
pssmRenderer.setLambda(pssmRenderer.getLambda() + 0.01f); pssmRenderer.setLambda(pssmRenderer.getLambda() + 0.01f);
System.out.println("Lambda : " + pssmRenderer.getLambda()); System.out.println("Lambda : " + pssmRenderer.getLambda());
@ -170,7 +259,13 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
pssmRenderer.setEdgesThickness(pssmRenderer.getEdgesThickness() - 1); pssmRenderer.setEdgesThickness(pssmRenderer.getEdgesThickness() - 1);
System.out.println("Shadow thickness : " + pssmRenderer.getEdgesThickness()); System.out.println("Shadow thickness : " + pssmRenderer.getEdgesThickness());
} }
} if (name.equals("switchGroundMat") && keyPressed) {
if(ground.getMaterial() == matGroundL){
ground.setMaterial(matGroundU);
}else{
ground.setMaterial(matGroundL);
}
}
}
} }

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

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

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

Loading…
Cancel
Save