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(); + } + } +}