From cbafa1852bb5909aa5588f530078db761cd3e826 Mon Sep 17 00:00:00 2001 From: "rem..om" Date: Sat, 29 Sep 2012 09:38:53 +0000 Subject: [PATCH] Shadows : There is now an alternative to the PssmRenderer : the PssmFilter that has to be used as any other filter. It does the same ass the PssmRenderer except the post shadow pass is done in screen space making it run very faster on scene that have a lot of shadow recieving Geometries. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9787 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../MatDefs/Shadow/PostShadowFilter.frag | 68 +++++ .../MatDefs/Shadow/PostShadowFilter.j3md | 73 +++++ .../MatDefs/Shadow/PostShadowFilter.vert | 10 + .../MatDefs/Shadow/PostShadowFilter15.frag | 84 ++++++ .../MatDefs/Shadow/PostShadowFilter15.vert | 12 + .../Common/MatDefs/Shadow/PostShadowPSSM.frag | 202 +------------- .../MatDefs/Shadow/PostShadowPSSM15.frag | 181 +----------- .../Common/ShaderLib/PssmShadows.glsllib | 151 ++++++++++ .../Common/ShaderLib/PssmShadows15.glsllib | 164 +++++++++++ .../com/jme3/shadow/PssmShadowFilter.java | 262 ++++++++++++++++++ .../com/jme3/shadow/PssmShadowRenderer.java | 108 +++++--- .../test/jme3test/light/TestPssmShadow.java | 86 ++++-- .../test/jme3test/light/TestShadowsPerf.java | 182 ++++++++++++ 13 files changed, 1169 insertions(+), 414 deletions(-) create mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag create mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md create mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.vert create mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag create mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.vert create mode 100644 engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib create mode 100644 engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib create mode 100644 engine/src/core/com/jme3/shadow/PssmShadowFilter.java create mode 100644 engine/src/test/jme3test/light/TestShadowsPerf.java diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag new file mode 100644 index 000000000..a11d73d5b --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag @@ -0,0 +1,68 @@ +#import "Common/ShaderLib/PssmShadows.glsllib" + +uniform sampler2D m_Texture; +uniform sampler2D m_DepthTexture; +uniform mat4 m_ViewProjectionMatrixInverse; +uniform vec4 m_ViewProjectionMatrixRow2; + +varying vec2 texCoord; + + +const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + +uniform mat4 m_LightViewProjectionMatrix0; +uniform mat4 m_LightViewProjectionMatrix1; +uniform mat4 m_LightViewProjectionMatrix2; +uniform mat4 m_LightViewProjectionMatrix3; + +vec3 getPosition(in float depth, in vec2 uv){ + vec4 pos = vec4(uv, depth, 1.0) * 2.0 - 1.0; + pos = m_ViewProjectionMatrixInverse * pos; + return pos.xyz / pos.w; +} + +void main(){ + + float depth = texture2D(m_DepthTexture,texCoord).r; + vec4 color = texture2D(m_Texture,texCoord); + + //Discard shadow computation on the sky + if(depth == 1.0){ + gl_FragColor = color; + return; + } + + // get the vertex in world space + vec4 worldPos = vec4(getPosition(depth,texCoord),1.0); + + // populate the light view matrices array and convert vertex to light viewProj space + vec4 projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos; + vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; + vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos; + vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos; + + + float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w; + + float shadow = 0.0; + if(shadowPosition < m_Splits.x){ + shadow = GETSHADOW(m_ShadowMap0, projCoord0); + }else if( shadowPosition < m_Splits.y){ + shadowBorderScale = 0.5; + shadow = GETSHADOW(m_ShadowMap1, projCoord1); + }else if( shadowPosition < m_Splits.z){ + shadowBorderScale = 0.25; + shadow = GETSHADOW(m_ShadowMap2, projCoord2); + }else if( shadowPosition < m_Splits.w){ + shadowBorderScale = 0.125; + shadow = GETSHADOW(m_ShadowMap3, projCoord3); + } + + shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); + gl_FragColor = color * vec4(shadow, shadow, shadow, 1.0); + +} + diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md new file mode 100644 index 000000000..1a611908a --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md @@ -0,0 +1,73 @@ +MaterialDef Post Shadow { + + MaterialParameters { + 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 + + Float ShadowMapSize + + Matrix4 ViewProjectionMatrixInverse + Vector4 ViewProjectionMatrixRow2 + + Int NumSamples + Int NumSamplesDepth + Texture2D Texture + Texture2D DepthTexture + + } + + Technique { + VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.vert + FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag + + WorldParameters { + WorldViewProjectionMatrix + } + + Defines { + RESOLVE_MS : NumSamples + RESOLVE_DEPTH_MS : NumSamplesDepth + HARDWARE_SHADOWS : HardwareShadows + FILTER_MODE : FilterMode + PCFEDGE : PCFEdge + SHADOWMAP_SIZE : ShadowMapSize + } + + } + + Technique { + VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowFilter.vert + FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowFilter.frag + + WorldParameters { + WorldViewProjectionMatrix + } + + Defines { + HARDWARE_SHADOWS : HardwareShadows + FILTER_MODE : FilterMode + PCFEDGE : PCFEdge + SHADOWMAP_SIZE : ShadowMapSize + } + + } + + + + +} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.vert b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.vert new file mode 100644 index 000000000..6d80905cd --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.vert @@ -0,0 +1,10 @@ +uniform mat4 g_WorldViewProjectionMatrix; + +attribute vec4 inPosition; +attribute vec2 inTexCoord; +varying vec2 texCoord; + +void main() { + gl_Position = inPosition * 2.0 - 1.0; //vec4(pos, 0.0, 1.0); + texCoord = inTexCoord; +} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag new file mode 100644 index 000000000..00832ae90 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag @@ -0,0 +1,84 @@ +#import "Common/ShaderLib/MultiSample.glsllib" +#import "Common/ShaderLib/PssmShadows15.glsllib" + + +uniform COLORTEXTURE m_Texture; +uniform DEPTHTEXTURE m_DepthTexture; +uniform mat4 m_ViewProjectionMatrixInverse; +uniform vec4 m_ViewProjectionMatrixRow2; + +in vec2 texCoord; +out vec4 outFragColor; + +const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + +uniform mat4 m_LightViewProjectionMatrix0; +uniform mat4 m_LightViewProjectionMatrix1; +uniform mat4 m_LightViewProjectionMatrix2; +uniform mat4 m_LightViewProjectionMatrix3; + + +vec3 getPosition(in float depth, in vec2 uv){ + vec4 pos = vec4(uv, depth, 1.0) * 2.0 - 1.0; + pos = m_ViewProjectionMatrixInverse * pos; + return pos.xyz / pos.w; +} + +vec4 main_multiSample(in int numSample){ + float depth = fetchTextureSample(m_DepthTexture,texCoord,numSample).r;//getDepth(m_DepthTexture,texCoord).r; + vec4 color = fetchTextureSample(m_Texture,texCoord,numSample); + + //Discard shadow computation on the sky + if(depth == 1.0){ + return color; + } + + // get the vertex in world space + vec4 worldPos = vec4(getPosition(depth,texCoord),1.0); + + // populate the light view matrices array and convert vertex to light viewProj space + vec4 projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos; + vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; + vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos; + vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos; + + float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w; + +float shadow = 0.0; + if(shadowPosition < m_Splits.x){ + shadow = GETSHADOW(m_ShadowMap0, projCoord0); + }else if( shadowPosition < m_Splits.y){ + shadowBorderScale = 0.5; + shadow = GETSHADOW(m_ShadowMap1, projCoord1); + }else if( shadowPosition < m_Splits.z){ + shadowBorderScale = 0.25; + shadow = GETSHADOW(m_ShadowMap2, projCoord2); + }else if( shadowPosition < m_Splits.w){ + shadowBorderScale = 0.125; + shadow = GETSHADOW(m_ShadowMap3, projCoord3); + } + + shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); + + return color * vec4(shadow, shadow, shadow, 1.0); +} + +void main(){ + + #ifdef RESOLVE_MS + vec4 color = vec4(0.0); + for (int i = 0; i < m_NumSamples; i++){ + color += main_multiSample(i); + } + outFragColor = color / m_NumSamples; + #else + outFragColor = main_multiSample(0); + #endif + +} + + + diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.vert b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.vert new file mode 100644 index 000000000..367c3306b --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.vert @@ -0,0 +1,12 @@ +uniform mat4 g_WorldViewProjectionMatrix; + +in vec4 inPosition; +in vec2 inTexCoord; + +out vec2 texCoord; + +void main() { + vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy; + gl_Position = vec4(pos, 0.0, 1.0); + texCoord = inTexCoord; +} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag index 17413d1ad..27e768d71 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag @@ -1,176 +1,11 @@ -#ifdef HARDWARE_SHADOWS - #define SHADOWMAP sampler2DShadow - #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r -#else - #define SHADOWMAP sampler2D - #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r) -#endif - -#if FILTER_MODE == 0 - #define GETSHADOW Shadow_DoShadowCompare - #define KERNEL 1.0 -#elif FILTER_MODE == 1 - #ifdef HARDWARE_SHADOWS - #define GETSHADOW Shadow_DoShadowCompare - #else - #define GETSHADOW Shadow_DoBilinear_2x2 - #endif - #define KERNEL 1.0 -#elif FILTER_MODE == 2 - #define GETSHADOW Shadow_DoDither_2x2 - #define KERNEL 1.0 -#elif FILTER_MODE == 3 - #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 - - -uniform SHADOWMAP m_ShadowMap0; -uniform SHADOWMAP m_ShadowMap1; -uniform SHADOWMAP m_ShadowMap2; -uniform SHADOWMAP m_ShadowMap3; - -uniform vec4 m_Splits; - -uniform float m_ShadowIntensity; +#import "Common/ShaderLib/PssmShadows.glsllib" +varying float shadowPosition; varying vec4 projCoord0; varying vec4 projCoord1; varying vec4 projCoord2; varying vec4 projCoord3; -varying float shadowPosition; - - -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); - return SHADOWCOMPARE(tex, coord); -} - -float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){ - return SHADOWCOMPARE(tex, projCoord); -} - -float Shadow_BorderCheck(in vec2 coord){ - // Fastest, "hack" method (uses 4-5 instructions) - vec4 t = vec4(coord.xy, 0.0, 1.0); - t = step(t.wwxy, t.xyzz); - return dot(t,t); -} - -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); - shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, 1.5) + o); - shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, -0.5) + o); - shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, -0.5) + o); - shadow *= 0.25 ; - return shadow; -} - -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)); - 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 * 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 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)) + - border, - 0.0, 1.0); - } - } - - shadow = shadow / (KERNEL * KERNEL); - return shadow; -} - - -//12 tap poisson disk -/* -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)); -*/ - const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016); - const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201); - const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521); - const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819); - const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957); - const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841); - const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516); - const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126); - const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693); - const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707); - const vec2 poissonDisk10 = vec2(0.5653144, 0.60262); - const vec2 poissonDisk11 = 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 , poissonDisk0 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk1 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk2 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk3 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk4 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk5 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk6 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk7 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk8 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk9 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk10 * texelSize); - shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk11 * texelSize); - - shadow = shadow * 0.08333333333;//this is divided by 12 - return shadow; -} - #ifdef DISCARD_ALPHA #ifdef COLOR_MAP uniform sampler2D m_ColorMap; @@ -194,37 +29,22 @@ void main(){ } #endif - - - vec4 shadowPerSplit = vec4(0.0); -float shadow; -//shadowPosition + + float shadow = 0.0; if(shadowPosition < m_Splits.x){ - shadow= GETSHADOW(m_ShadowMap0, projCoord0); + shadow = GETSHADOW(m_ShadowMap0, projCoord0); }else if( shadowPosition < m_Splits.y){ - scale = 0.5; + shadowBorderScale = 0.5; shadow = GETSHADOW(m_ShadowMap1, projCoord1); }else if( shadowPosition < m_Splits.z){ - scale = 0.25; - shadow= GETSHADOW(m_ShadowMap2, projCoord2); + shadowBorderScale = 0.25; + shadow = GETSHADOW(m_ShadowMap2, projCoord2); }else if( shadowPosition < m_Splits.w){ - scale = 0.125; - shadow= GETSHADOW(m_ShadowMap3, projCoord3); + shadowBorderScale = 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(shadow, shadow, shadow, 1.0); diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag index 816e9387c..c2c256078 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag @@ -1,172 +1,11 @@ -// Because gpu_shader5 is actually where those -// gather functions are declared to work on shadowmaps -#extension GL_ARB_gpu_shader5 : enable - -#ifdef HARDWARE_SHADOWS - #define SHADOWMAP sampler2DShadow - #define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset) - #define SHADOWCOMPARE(tex,coord) textureProj(tex, coord) - #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z) -#else - #define SHADOWMAP sampler2D - #define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r) - #define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r) - #define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy)) -#endif - - -#if FILTER_MODE == 0 - #define GETSHADOW SHADOWCOMPARE - #define KERNEL 1 -#elif FILTER_MODE == 1 - #ifdef HARDWARE_SHADOWS - #define GETSHADOW SHADOWCOMPARE - #else - #define GETSHADOW Shadow_DoBilinear_2x2 - #endif - #define KERNEL 1 -#elif FILTER_MODE == 2 - #define GETSHADOW Shadow_DoDither_2x2 - #define KERNEL 1 -#elif FILTER_MODE == 3 - #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 +#import "Common/ShaderLib/PssmShadows15.glsllib" out vec4 outFragColor; - -uniform SHADOWMAP m_ShadowMap0; -uniform SHADOWMAP m_ShadowMap1; -uniform SHADOWMAP m_ShadowMap2; -uniform SHADOWMAP m_ShadowMap3; - -uniform vec4 m_Splits; -uniform float m_ShadowIntensity; - +in float shadowPosition; in vec4 projCoord0; 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) - vec4 t = vec4(coord.xy, 0.0, 1.0); - t = step(t.wwxy, t.xyzz); - return dot(t,t); -} - -float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ - float border = Shadow_BorderCheck(projCoord.xy); - if (border > 0.0) - return 1.0; - - 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)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, 1.5)+o), projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, -0.5)+o), projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, -0.5)+o), projCoord.zw)); - shadow *= 0.25; - return shadow; -} - -float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ - float border = Shadow_BorderCheck(projCoord.xy); - if (border > 0.0) - return 1.0; - - #ifdef GL_ARB_gpu_shader5 - vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0); - vec4 gather = SHADOWGATHER(tex, coord); - #else - vec4 gather = vec4(0.0); - gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0)); - gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0)); - gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1)); - gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1)); - #endif - - 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){ - - vec2 pixSize = pixSize2 * scale; - 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){ - vec4 coord = vec4(projCoord.xy + vec2(x,y) * pixSize, projCoord.zw); - shadow += SHADOWCOMPARE(tex, coord); - } - } - - shadow = shadow / (KERNEL * KERNEL); - return shadow; -} - - -//12 tap poisson disk - const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016); - const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201); - const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521); - const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819); - const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957); - const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841); - const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516); - const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126); - const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693); - const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707); - const vec2 poissonDisk10 = vec2(0.5653144, 0.60262); - const vec2 poissonDisk11 = 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 + poissonDisk0 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw)); - shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw)); - - shadow = shadow * 0.08333333333;//this is divided by 12 - return shadow; -} #ifdef DISCARD_ALPHA #ifdef COLOR_MAP @@ -179,9 +18,7 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){ #endif void main(){ - float shadow = 0.0; - - + #ifdef DISCARD_ALPHA #ifdef COLOR_MAP float alpha = texture2D(m_ColorMap,texCoord).a; @@ -193,20 +30,24 @@ void main(){ discard; } #endif + + + float shadow = 0.0; if(shadowPosition < m_Splits.x){ shadow = GETSHADOW(m_ShadowMap0, projCoord0); }else if( shadowPosition < m_Splits.y){ - scale = 0.5; + shadowBorderScale = 0.5; shadow = GETSHADOW(m_ShadowMap1, projCoord1); }else if( shadowPosition < m_Splits.z){ - scale = 0.25; + shadowBorderScale = 0.25; shadow = GETSHADOW(m_ShadowMap2, projCoord2); }else if( shadowPosition < m_Splits.w){ - scale = 0.125; + shadowBorderScale = 0.125; shadow = GETSHADOW(m_ShadowMap3, projCoord3); } - shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); + shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); + outFragColor = vec4(shadow, shadow, shadow, 1.0); } diff --git a/engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib b/engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib new file mode 100644 index 000000000..1e53d86f3 --- /dev/null +++ b/engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib @@ -0,0 +1,151 @@ +#ifdef HARDWARE_SHADOWS + #define SHADOWMAP sampler2DShadow + #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r +#else + #define SHADOWMAP sampler2D + #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r) +#endif + +#if FILTER_MODE == 0 + #define GETSHADOW Shadow_DoShadowCompare + #define KERNEL 1.0 +#elif FILTER_MODE == 1 + #ifdef HARDWARE_SHADOWS + #define GETSHADOW Shadow_DoShadowCompare + #else + #define GETSHADOW Shadow_DoBilinear_2x2 + #endif + #define KERNEL 1.0 +#elif FILTER_MODE == 2 + #define GETSHADOW Shadow_DoDither_2x2 + #define KERNEL 1.0 +#elif FILTER_MODE == 3 + #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 + + +uniform SHADOWMAP m_ShadowMap0; +uniform SHADOWMAP m_ShadowMap1; +uniform SHADOWMAP m_ShadowMap2; +uniform SHADOWMAP m_ShadowMap3; + +uniform vec4 m_Splits; + +uniform float m_ShadowIntensity; + +const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE); +float shadowBorderScale = 1.0; + +float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, in vec4 projCoord, in vec2 offset){ + vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2 * shadowBorderScale, projCoord.zw); + return SHADOWCOMPARE(tex, coord); +} + +float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){ + return SHADOWCOMPARE(tex, projCoord); +} + +float Shadow_BorderCheck(in vec2 coord){ + // Fastest, "hack" method (uses 4-5 instructions) + vec4 t = vec4(coord.xy, 0.0, 1.0); + t = step(t.wwxy, t.xyzz); + return dot(t,t); +} + +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); + shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, 1.5) + o); + shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, -0.5) + o); + shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, -0.5) + o); + shadow *= 0.25 ; + return shadow; +} + +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)); + 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 * 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 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)) + + border, + 0.0, 1.0); + } + } + + shadow = shadow / (KERNEL * KERNEL); + return shadow; +} + + +//12 tap poisson disk + const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016); + const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201); + const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521); + const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819); + const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957); + const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841); + const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516); + const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126); + const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693); + const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707); + const vec2 poissonDisk10 = vec2(0.5653144, 0.60262); + const vec2 poissonDisk11 = 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 * shadowBorderScale); + + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk0 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk1 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk2 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk3 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk4 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk5 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk6 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk7 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk8 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk9 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk10 * texelSize); + shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk11 * texelSize); + + shadow = shadow * 0.08333333333;//this is divided by 12 + return shadow; +} + diff --git a/engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib b/engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib new file mode 100644 index 000000000..f52213003 --- /dev/null +++ b/engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib @@ -0,0 +1,164 @@ +// Because gpu_shader5 is actually where those +// gather functions are declared to work on shadowmaps +#extension GL_ARB_gpu_shader5 : enable + +#ifdef HARDWARE_SHADOWS + #define SHADOWMAP sampler2DShadow + #define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset) + #define SHADOWCOMPARE(tex,coord) textureProj(tex, coord) + #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z) +#else + #define SHADOWMAP sampler2D + #define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r) + #define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r) + #define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy)) +#endif + + +#if FILTER_MODE == 0 + #define GETSHADOW SHADOWCOMPARE + #define KERNEL 1 +#elif FILTER_MODE == 1 + #ifdef HARDWARE_SHADOWS + #define GETSHADOW SHADOWCOMPARE + #else + #define GETSHADOW Shadow_DoBilinear_2x2 + #endif + #define KERNEL 1 +#elif FILTER_MODE == 2 + #define GETSHADOW Shadow_DoDither_2x2 + #define KERNEL 1 +#elif FILTER_MODE == 3 + #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 + + + +uniform SHADOWMAP m_ShadowMap0; +uniform SHADOWMAP m_ShadowMap1; +uniform SHADOWMAP m_ShadowMap2; +uniform SHADOWMAP m_ShadowMap3; + +uniform vec4 m_Splits; +uniform float m_ShadowIntensity; + +const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE); +float shadowBorderScale = 1.0; + +float Shadow_BorderCheck(in vec2 coord){ + // Fastest, "hack" method (uses 4-5 instructions) + vec4 t = vec4(coord.xy, 0.0, 1.0); + t = step(t.wwxy, t.xyzz); + return dot(t,t); +} + +float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ + float border = Shadow_BorderCheck(projCoord.xy); + if (border > 0.0) + return 1.0; + + vec2 pixSize = pixSize2 * shadowBorderScale; + + 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)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, 1.5)+o), projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, -0.5)+o), projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, -0.5)+o), projCoord.zw)); + shadow *= 0.25; + return shadow; +} + +float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ + float border = Shadow_BorderCheck(projCoord.xy); + if (border > 0.0) + return 1.0; + + #ifdef GL_ARB_gpu_shader5 + vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0); + vec4 gather = SHADOWGATHER(tex, coord); + #else + vec4 gather = vec4(0.0); + gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0)); + gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0)); + gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1)); + gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1)); + #endif + + 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){ + + vec2 pixSize = pixSize2 * shadowBorderScale; + 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){ + vec4 coord = vec4(projCoord.xy + vec2(x,y) * pixSize, projCoord.zw); + shadow += SHADOWCOMPARE(tex, coord); + } + } + + shadow = shadow / (KERNEL * KERNEL); + return shadow; +} + + +//12 tap poisson disk + const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016); + const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201); + const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521); + const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819); + const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957); + const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841); + const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516); + const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126); + const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693); + const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707); + const vec2 poissonDisk10 = vec2(0.5653144, 0.60262); + const vec2 poissonDisk11 = 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 = pixSize2 * 4.0 * PCFEDGE * shadowBorderScale; + + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk0 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw)); + shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw)); + + //this is divided by 12 + return shadow * 0.08333333333; +} + + diff --git a/engine/src/core/com/jme3/shadow/PssmShadowFilter.java b/engine/src/core/com/jme3/shadow/PssmShadowFilter.java new file mode 100644 index 000000000..342f4f2c2 --- /dev/null +++ b/engine/src/core/com/jme3/shadow/PssmShadowFilter.java @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.shadow; + +import com.jme3.asset.AssetManager; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.material.Material; +import com.jme3.math.Matrix4f; +import com.jme3.math.Vector3f; +import com.jme3.math.Vector4f; +import com.jme3.post.Filter; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.RenderQueue; +import com.jme3.shadow.PssmShadowRenderer.CompareMode; +import com.jme3.shadow.PssmShadowRenderer.FilterMode; +import com.jme3.texture.FrameBuffer; +import java.io.IOException; + +/** + * + * This Filter does basically the same as a PssmShadowRenderer except it renders + * the post shadow pass as a fulscreen quad pass instead of a geometry pass. + * It's mostly faster than PssmShadowRenderer as long as you have more than a about ten shadow recieving objects. + * The expense is the draw back that the shadow Recieve mode set on spatial is ignored. + * So basically all and only objects that render depth in the scene receive shadows. + * See this post for more details http://jmonkeyengine.org/groups/general-2/forum/topic/silly-question-about-shadow-rendering/#post-191599 + * + * API is basically the same as the PssmShadowRenderer; + * + * @author Rémy Bouquet aka Nehon + */ +public class PssmShadowFilter extends Filter { + + private PssmShadowRenderer pssmRenderer; + private ViewPort viewPort; + + /** + * Creates a PSSM Shadow Filter + * More info on the technique at http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html + * @param manager the application asset manager + * @param size the size of the rendered shadowmaps (512,1024,2048, etc...) + * @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps). + */ + public PssmShadowFilter(AssetManager manager, int size, int nbSplits) { + super("Post Shadow"); + material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); + pssmRenderer = new PssmShadowRenderer(manager, size, nbSplits, material); + pssmRenderer.needsfallBackMaterial = true; + } + + @Override + protected Material getMaterial() { + return material; + } + + @Override + protected boolean isRequiresDepthTexture() { + return true; + } + + public Material getShadowMaterial() { + return material; + } + Vector4f tmpv = new Vector4f(); + + @Override + protected void preFrame(float tpf) { + pssmRenderer.preFrame(tpf); + material.setMatrix4("ViewProjectionMatrixInverse", viewPort.getCamera().getViewProjectionMatrix().invert()); + Matrix4f m = viewPort.getCamera().getViewProjectionMatrix(); + material.setVector4("ViewProjectionMatrixRow2", tmpv.set(m.m20, m.m21, m.m22, m.m23)); + + } + + @Override + protected void postQueue(RenderQueue queue) { + pssmRenderer.postQueue(queue); + } + + @Override + protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { + pssmRenderer.setPostShadowParams(); + } + + @Override + protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { + pssmRenderer.initialize(renderManager, vp); + this.viewPort = vp; + } + + /** + * returns the light direction used by the processor + * @return + */ + public Vector3f getDirection() { + return pssmRenderer.getDirection(); + } + + /** + * Sets the light direction to use to compute shadows + * @param direction + */ + public void setDirection(Vector3f direction) { + pssmRenderer.setDirection(direction); + } + + /** + * returns the labda parameter
+ * see {@link setLambda(float lambda)} + * @return lambda + */ + public float getLambda() { + return pssmRenderer.getLambda(); + } + + /* + * Adjust the repartition of the different shadow maps in the shadow extend + * usualy goes from 0.0 to 1.0 + * a low value give a more linear repartition resulting in a constant quality in the shadow over the extends, but near shadows could look very jagged + * a high value give a more logarithmic repartition resulting in a high quality for near shadows, but the quality quickly decrease over the extend. + * the default value is set to 0.65f (theoric optimal value). + * @param lambda the lambda value. + */ + public void setLambda(float lambda) { + pssmRenderer.setLambda(lambda); + } + + /** + * How far the shadows are rendered in the view + * see {@link setShadowZExtend(float zFar)} + * @return shadowZExtend + */ + public float getShadowZExtend() { + return pssmRenderer.getShadowZExtend(); + } + + /** + * Set the distance from the eye where the shadows will be rendered + * default value is dynamicaly computed to the shadow casters/receivers union bound zFar, capped to view frustum far value. + * @param zFar the zFar values that override the computed one + */ + public void setShadowZExtend(float zFar) { + pssmRenderer.setShadowZExtend(zFar); + } + + /** + * returns the shdaow intensity
+ * see {@link setShadowIntensity(float shadowIntensity)} + * @return shadowIntensity + */ + public float getShadowIntensity() { + return pssmRenderer.getShadowIntensity(); + } + + /** + * Set the shadowIntensity, the value should be between 0 and 1, + * a 0 value gives a bright and invisilble shadow, + * a 1 value gives a pitch black shadow, + * default is 0.7 + * @param shadowIntensity the darkness of the shadow + */ + final public void setShadowIntensity(float shadowIntensity) { + pssmRenderer.setShadowIntensity(shadowIntensity); + } + + /** + * returns the edges thickness
+ * see {@link setEdgesThickness(int edgesThickness)} + * @return edgesThickness + */ + public int getEdgesThickness() { + return pssmRenderer.getEdgesThickness(); + } + + /** + * Sets the shadow edges thickness. default is 1, setting it to lower values can help to reduce the jagged effect of the shadow edges + * @param edgesThickness + */ + public void setEdgesThickness(int edgesThickness) { + pssmRenderer.setEdgesThickness(edgesThickness); + } + + /** + * returns true if the PssmRenderer flushed the shadow queues + * @return flushQueues + */ + public boolean isFlushQueues() { + return pssmRenderer.isFlushQueues(); + } + + /** + * Set this to false if you want to use several PssmRederers to have multiple shadows cast by multiple light sources. + * Make sure the last PssmRenderer in the stack DO flush the queues, but not the others + * @param flushQueues + */ + public void setFlushQueues(boolean flushQueues) { + pssmRenderer.setFlushQueues(flushQueues); + } + + /** + * sets the shadow compare mode see {@link CompareMode} for more info + * @param compareMode + */ + final public void setCompareMode(CompareMode compareMode) { + pssmRenderer.setCompareMode(compareMode); + } + + /** + * Sets the filtering mode for shadow edges see {@link FilterMode} for more info + * @param filterMode + */ + final public void setFilterMode(FilterMode filterMode) { + pssmRenderer.setFilterMode(filterMode); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule ic = im.getCapsule(this); + + } +} diff --git a/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java b/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java index 2749e36f9..1576c937d 100644 --- a/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java +++ b/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java @@ -31,7 +31,6 @@ 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; @@ -48,7 +47,6 @@ 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; @@ -56,6 +54,8 @@ import com.jme3.texture.Texture.MinFilter; import com.jme3.texture.Texture.ShadowCompareMode; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; +import java.util.ArrayList; +import java.util.List; /** * PssmShadow renderer use Parrallel Split Shadow Mapping technique (pssm)
@@ -99,13 +99,12 @@ public class PssmShadowRenderer implements SceneProcessor { * 8x8 percentage-closer filtering is used. Shadows will be smoother * at the cost of performance */ - PCFPOISSON, + PCFPOISSON, /** * 8x8 percentage-closer filtering is used. Shadows will be smoother * at the cost of performance */ PCF8 - } /** @@ -151,9 +150,16 @@ public class PssmShadowRenderer implements SceneProcessor { private Vector3f[] points = new Vector3f[8]; private boolean flushQueues = true; // define if the fallback material should be used. - private boolean needsfallBackMaterial = false; + protected boolean needsfallBackMaterial = false; //Name of the post material technique private String postTechniqueName = "PostShadow"; + //flags to know when to change params in the materials + private boolean applyHWShadows = true; + private boolean applyFilterMode = true; + private boolean applyPCFEdge = true; + private boolean applyShadowIntensity = true; + //a list of material of the post shadow queue geometries. + private List matCache = new ArrayList(); /** * Create a PSSM Shadow Renderer @@ -165,7 +171,6 @@ public class PssmShadowRenderer implements SceneProcessor { */ public PssmShadowRenderer(AssetManager manager, int size, int nbSplits) { this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md")); - } /** @@ -174,10 +179,12 @@ public class PssmShadowRenderer implements SceneProcessor { * @param manager the application asset manager * @param size the size of the rendered shadowmaps (512,1024,2048, etc...) * @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps). - * @param postShadowMat the material used for post shadows if you need to override it * + * @param filterPass set this to true if you want the post shadow pass to be done in a Filter un screen space. + * @param postShadowMat the material used for post shadows if you need to override it */ - //TODO remove the postShadowMat when we have shader injection....or remove this todo if we are in 2020. - public PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Material postShadowMat) { + protected PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Material postShadowMat) { + + this.postshadowMat = postShadowMat; assetManager = manager; nbSplits = Math.max(Math.min(nbSplits, 4), 1); this.nbSplits = nbSplits; @@ -194,9 +201,8 @@ public class PssmShadowRenderer implements SceneProcessor { dummyTex = new Texture2D(size, size, Format.RGBA8); preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md"); - this.postshadowMat = postShadowMat; - postshadowMat.setFloat("ShadowMapSize", size); - + postshadowMat.setFloat("ShadowMapSize", size); + for (int i = 0; i < nbSplits; i++) { lightViewProjectionsMatrices[i] = new Matrix4f(); shadowFB[i] = new FrameBuffer(size, size, 1); @@ -254,6 +260,7 @@ public class PssmShadowRenderer implements SceneProcessor { } } } + applyFilterMode = true; } /** @@ -287,6 +294,7 @@ public class PssmShadowRenderer implements SceneProcessor { } } postshadowMat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); + applyHWShadows = true; } //debug function that create a displayable frustrum @@ -429,12 +437,12 @@ public class PssmShadowRenderer implements SceneProcessor { } //debug only : displays depth shadow maps - private void displayShadowMap(Renderer r) { + protected void displayShadowMap(Renderer r) { Camera cam = viewPort.getCamera(); renderManager.setCamera(cam, true); int h = cam.getHeight(); for (int i = 0; i < dispPic.length; i++) { - dispPic[i].setPosition((128 * i) +(150 + 64 * (i + 1) ), h / 20f); + dispPic[i].setPosition((128 * i) + (150 + 64 * (i + 1)), h / 20f); dispPic[i].setWidth(128); dispPic[i].setHeight(128); dispPic[i].updateGeometricState(); @@ -451,10 +459,15 @@ public class PssmShadowRenderer implements SceneProcessor { } public void postFrame(FrameBuffer out) { - Camera cam = viewPort.getCamera(); + + if (debug) { + displayShadowMap(renderManager.getRenderer()); + } if (!noOccluders) { //setting params to recieving geometry list setMatParams(); + + Camera cam = viewPort.getCamera(); //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); @@ -472,50 +485,79 @@ public class PssmShadowRenderer implements SceneProcessor { renderManager.setCamera(cam, false); } - if (debug) { - displayShadowMap(renderManager.getRenderer()); - } + } private void setMatParams() { GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive); - //iteratin throught all the geometries of the list to set the material params + //iteration throught all the geometries of the list to gather the materials + + matCache.clear(); 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. + //checking if the material has the post technique and adding it to the material cache if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) { + if (!matCache.contains(mat)) { + matCache.add(mat); + } + } else { + needsfallBackMaterial = true; + } + } + + //iterating through the mat cache and setting the parameters + for (Material mat : matCache) { + if (mat.getParam("Splits") == null) { mat.setColor("Splits", splits); - postshadowMat.setColor("Splits", splits); + } + if (mat.getParam("ShadowMapSize") == null) { + mat.setFloat("ShadowMapSize", shadowMapSize); + } + for (int j = 0; j < nbSplits; j++) { + mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); + } + if (mat.getParam("ShadowMap0") == null) { for (int j = 0; j < nbSplits; j++) { - mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); mat.setTexture("ShadowMap" + j, shadowMaps[j]); } + } + if (applyHWShadows || mat.getParam("HardwareShadows") == null) { mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); + applyHWShadows = false; + } + if (applyFilterMode || mat.getParam("FilterMode") == null) { mat.setInt("FilterMode", filterMode.ordinal()); + applyFilterMode = false; + } + if (mat.getParam("PCFEdge") == null || applyPCFEdge) { mat.setFloat("PCFEdge", edgesThickness); + applyPCFEdge = false; + } + + if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) { mat.setFloat("ShadowIntensity", shadowIntensity); - if(mat.getParam("ShadowMapSize") == null){ - mat.setFloat("ShadowMapSize", shadowMapSize); - } - } else { - needsfallBackMaterial = true; + applyShadowIntensity = false; } - } + } //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]); - } + setPostShadowParams(); } } + protected void setPostShadowParams() { + postshadowMat.setColor("Splits", splits); + for (int j = 0; j < nbSplits; j++) { + postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); + } + } + public void preFrame(float tpf) { } @@ -583,6 +625,7 @@ public class PssmShadowRenderer implements SceneProcessor { final public void setShadowIntensity(float shadowIntensity) { this.shadowIntensity = shadowIntensity; postshadowMat.setFloat("ShadowIntensity", shadowIntensity); + applyShadowIntensity = true; } /** @@ -602,6 +645,7 @@ public class PssmShadowRenderer implements SceneProcessor { this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10)); this.edgesThickness *= 0.1f; postshadowMat.setFloat("PCFEdge", edgesThickness); + applyPCFEdge = true; } /** diff --git a/engine/src/test/jme3test/light/TestPssmShadow.java b/engine/src/test/jme3test/light/TestPssmShadow.java index 005675627..08e58b7af 100644 --- a/engine/src/test/jme3test/light/TestPssmShadow.java +++ b/engine/src/test/jme3test/light/TestPssmShadow.java @@ -44,6 +44,8 @@ import com.jme3.math.FastMath; import com.jme3.math.Quaternion; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; +import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.FXAAFilter; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue.ShadowMode; @@ -53,6 +55,7 @@ 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.PssmShadowFilter; import com.jme3.shadow.PssmShadowRenderer; import com.jme3.shadow.PssmShadowRenderer.CompareMode; import com.jme3.shadow.PssmShadowRenderer.FilterMode; @@ -65,13 +68,12 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener private Spatial[] obj; private Material[] mat; - private boolean renderShadows = true; private boolean hardwareShadows = false; private PssmShadowRenderer pssmRenderer; + private PssmShadowFilter pssmFilter; private Geometry ground; - private Material matGroundU; - private Material matGroundL; - + private Material matGroundU; + private Material matGroundL; public static void main(String[] args) { TestPssmShadow app = new TestPssmShadow(); @@ -109,15 +111,15 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener 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); @@ -146,16 +148,35 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener loadScene(); pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 3); + // pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); pssmRenderer.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal()); pssmRenderer.setLambda(0.55f); pssmRenderer.setShadowIntensity(0.6f); pssmRenderer.setCompareMode(CompareMode.Software); pssmRenderer.setFilterMode(FilterMode.Dither); - pssmRenderer.displayDebug(); + // pssmRenderer.displayDebug(); viewPort.addProcessor(pssmRenderer); + + + + pssmFilter = new PssmShadowFilter(assetManager, 1024, 3); + //pssmFilter.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); + pssmRenderer.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal()); + pssmFilter.setLambda(0.55f); + pssmFilter.setShadowIntensity(0.6f); + pssmFilter.setCompareMode(CompareMode.Software); + pssmFilter.setFilterMode(FilterMode.Dither); + pssmFilter.setEnabled(false); + FilterPostProcessor fpp = new FilterPostProcessor(assetManager); + // fpp.setNumSamples(4); + fpp.addFilter(pssmFilter); + + viewPort.addProcessor(fpp); + + initInputs(); } - BitmapText infoText; + BitmapText infoText; private void initInputs() { /** Write text on the screen (HUD) */ @@ -174,7 +195,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J)); inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN)); inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M)); - inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown","changeFiltering","switchGroundMat"); + inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering", "switchGroundMat"); } private void print(String str) { @@ -212,60 +233,83 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener } }; int filteringIndex = 2; + int renderModeIndex = 0; public void onAction(String name, boolean keyPressed, float tpf) { if (name.equals("toggle") && keyPressed) { - if (renderShadows) { - renderShadows = false; - viewPort.removeProcessor(pssmRenderer); - } else { - renderShadows = true; - viewPort.addProcessor(pssmRenderer); + renderModeIndex += 1; + renderModeIndex %= 3; + + switch (renderModeIndex) { + case 0: + viewPort.addProcessor(pssmRenderer); + break; + case 1: + viewPort.removeProcessor(pssmRenderer); + pssmFilter.setEnabled(true); + break; + case 2: + pssmFilter.setEnabled(false); + break; } + + + } else if (name.equals("toggleHW") && keyPressed) { hardwareShadows = !hardwareShadows; pssmRenderer.setCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software); + pssmFilter.setCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software); System.out.println("HW Shadows: " + hardwareShadows); } +// +// renderShadows = true; +// viewPort.addProcessor(pssmRenderer); if (name.equals("changeFiltering") && keyPressed) { filteringIndex = (filteringIndex + 1) % FilterMode.values().length; FilterMode m = FilterMode.values()[filteringIndex]; pssmRenderer.setFilterMode(m); + pssmFilter.setFilterMode(m); print("Filter mode : " + m.toString()); } if (name.equals("lambdaUp") && keyPressed) { pssmRenderer.setLambda(pssmRenderer.getLambda() + 0.01f); + pssmFilter.setLambda(pssmRenderer.getLambda() + 0.01f); System.out.println("Lambda : " + pssmRenderer.getLambda()); } else if (name.equals("lambdaDown") && keyPressed) { pssmRenderer.setLambda(pssmRenderer.getLambda() - 0.01f); + pssmFilter.setLambda(pssmRenderer.getLambda() - 0.01f); System.out.println("Lambda : " + pssmRenderer.getLambda()); } if (name.equals("ShadowUp") && keyPressed) { pssmRenderer.setShadowIntensity(pssmRenderer.getShadowIntensity() + 0.1f); + pssmFilter.setShadowIntensity(pssmRenderer.getShadowIntensity() + 0.1f); System.out.println("Shadow intensity : " + pssmRenderer.getShadowIntensity()); } if (name.equals("ShadowDown") && keyPressed) { pssmRenderer.setShadowIntensity(pssmRenderer.getShadowIntensity() - 0.1f); + pssmFilter.setShadowIntensity(pssmRenderer.getShadowIntensity() - 0.1f); System.out.println("Shadow intensity : " + pssmRenderer.getShadowIntensity()); } if (name.equals("ThicknessUp") && keyPressed) { pssmRenderer.setEdgesThickness(pssmRenderer.getEdgesThickness() + 1); + pssmFilter.setEdgesThickness(pssmRenderer.getEdgesThickness() + 1); System.out.println("Shadow thickness : " + pssmRenderer.getEdgesThickness()); } if (name.equals("ThicknessDown") && keyPressed) { pssmRenderer.setEdgesThickness(pssmRenderer.getEdgesThickness() - 1); + pssmFilter.setEdgesThickness(pssmRenderer.getEdgesThickness() - 1); System.out.println("Shadow thickness : " + pssmRenderer.getEdgesThickness()); } if (name.equals("switchGroundMat") && keyPressed) { - if(ground.getMaterial() == matGroundL){ + if (ground.getMaterial() == matGroundL) { ground.setMaterial(matGroundU); - }else{ + } else { ground.setMaterial(matGroundL); - } + } } - + } } diff --git a/engine/src/test/jme3test/light/TestShadowsPerf.java b/engine/src/test/jme3test/light/TestShadowsPerf.java new file mode 100644 index 000000000..503bc2127 --- /dev/null +++ b/engine/src/test/jme3test/light/TestShadowsPerf.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package jme3test.light; + +import com.jme3.app.SimpleApplication; +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.light.PointLight; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.Geometry; +import com.jme3.scene.Spatial; +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 com.jme3.util.TangentBinormalGenerator; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class TestShadowsPerf extends SimpleApplication { + + float angle; + PointLight pl; + Spatial lightMdl; + + public static void main(String[] args) { + TestShadowsPerf app = new TestShadowsPerf(); + app.start(); + } + Geometry sphere; + Material mat; + + @Override + public void simpleInitApp() { + Logger.getLogger("com.jme3").setLevel(Level.SEVERE); + flyCam.setMoveSpeed(50); + flyCam.setEnabled(false); + viewPort.setBackgroundColor(ColorRGBA.DarkGray); + cam.setLocation(new Vector3f(-53.952988f, 27.15874f, -32.875023f)); + cam.setRotation(new Quaternion(0.1564309f, 0.6910534f, -0.15713608f, 0.6879555f)); + +// cam.setLocation(new Vector3f(53.64627f, 130.56f, -11.247704f)); +// cam.setRotation(new Quaternion(-6.5737107E-4f, 0.76819664f, -0.64021313f, -7.886125E-4f)); +//// + cam.setFrustumFar(500); + + mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); + + Box b = new Box(Vector3f.ZERO, 800, 1, 700); + b.scaleTextureCoordinates(new Vector2f(50, 50)); + Geometry ground = new Geometry("ground", b); + ground.setMaterial(mat); + rootNode.attachChild(ground); + ground.setShadowMode(ShadowMode.Receive); + + Sphere sphMesh = new Sphere(32, 32, 1); + sphMesh.setTextureMode(Sphere.TextureMode.Projected); + sphMesh.updateGeometry(32, 32, 1, false, false); + TangentBinormalGenerator.generate(sphMesh); + + sphere = new Geometry("Rock Ball", sphMesh); + sphere.setLocalTranslation(0, 5, 0); + sphere.setMaterial(mat); + sphere.setShadowMode(ShadowMode.CastAndReceive); + rootNode.attachChild(sphere); + + + + + DirectionalLight dl = new DirectionalLight(); + dl.setDirection(new Vector3f(0, -1, 0).normalizeLocal()); + dl.setColor(ColorRGBA.White); + rootNode.addLight(dl); + + AmbientLight al = new AmbientLight(); + al.setColor(ColorRGBA.White.mult(0.7f)); + rootNode.addLight(al); + //rootNode.setShadowMode(ShadowMode.CastAndReceive); + + createballs(); + + final PssmShadowRenderer pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 4); + viewPort.addProcessor(pssmRenderer); +// +// final PssmShadowFilter pssmRenderer = new PssmShadowFilter(assetManager, 1024, 4); +// FilterPostProcessor fpp = new FilterPostProcessor(assetManager); +// fpp.addFilter(pssmRenderer); +// viewPort.addProcessor(fpp); + + pssmRenderer.setDirection(dl.getDirection()); + pssmRenderer.setLambda(0.55f); + pssmRenderer.setShadowIntensity(0.55f); + pssmRenderer.setCompareMode(CompareMode.Software); + pssmRenderer.setFilterMode(FilterMode.PCF4); + //pssmRenderer.displayDebug(); + + inputManager.addListener(new ActionListener() { + + public void onAction(String name, boolean isPressed, float tpf) { + if (name.equals("display") && isPressed) { + //pssmRenderer.debugFrustrums(); + System.out.println("tetetetet"); + } + if (name.equals("add") && isPressed) { + createballs(); + } + } + }, "display", "add"); + inputManager.addMapping("display", new KeyTrigger(KeyInput.KEY_SPACE)); + inputManager.addMapping("add", new KeyTrigger(KeyInput.KEY_RETURN)); + } + int val = 0; + + private void createballs() { + System.out.println((frames / time) + ";" + val); + + + for (int i = val; i < val + 200; i++) { + + Geometry s = sphere.clone().clone(false); + s.setMaterial(mat); + s.setLocalTranslation(i - 30, 5, (((i) * 2) % 40) - 50); + s.setShadowMode(ShadowMode.CastAndReceive); + rootNode.attachChild(s); + } + if (val == 300) { + //stop(); + } + val += 1; + time = 0; + frames = 0; + } + float time; + float frames = 0; + + @Override + public void simpleUpdate(float tpf) { + time += tpf; + frames++; + if (time > 1) { + //createballs(); + } + } +}