From 0dadaa80f5b362ce3f4275a7d79a6360d573b0c9 Mon Sep 17 00:00:00 2001 From: "rem..om" Date: Wed, 7 Nov 2012 08:38:56 +0000 Subject: [PATCH] Shadow system refactoring. - Basic and PSSM shadow renderer are now deprecated - There is now one processor and its filter conterpart for each light type - created an abstract shadow processor that hold the common shadowing code. It's totally independent of the shadow technique used. - extracted the CompareMode and FilterMode enum to their own files. - renamed FilterMode enum to EdgeFilteringMode - refactored the shader code, to avoid duplicate code. all shadow related code is now gathered into Shadows.glsllib and Shadows15.glsllib. - added spot light Shadows - removed the ShadowCamera class as it was not used. - removed "pssm" in the naming of classes, shader and shader libs since it's not relevant anymore git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9971 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../Common/MatDefs/Light/Lighting.j3md | 8 +- .../MatDefs/Shadow/BasicPostShadow.frag | 12 + .../MatDefs/Shadow/BasicPostShadow.j3md | 28 + .../MatDefs/Shadow/BasicPostShadow.vert | 31 + .../Common/MatDefs/Shadow/PostShadow.frag | 84 ++- .../Common/MatDefs/Shadow/PostShadow.j3md | 63 +- .../Common/MatDefs/Shadow/PostShadow.vert | 71 +- .../Common/MatDefs/Shadow/PostShadow15.frag | 72 ++ .../MatDefs/Shadow/PostShadowFilter.frag | 55 +- .../MatDefs/Shadow/PostShadowFilter.j3md | 8 +- .../MatDefs/Shadow/PostShadowFilter15.frag | 58 +- .../Common/MatDefs/Shadow/PostShadowPSSM.frag | 96 --- .../Common/MatDefs/Shadow/PostShadowPSSM.j3md | 85 --- .../Common/MatDefs/Shadow/PostShadowPSSM.vert | 62 -- .../MatDefs/Shadow/PostShadowPSSM15.frag | 105 --- .../{Shadow.glsllib => BasicShadow.glsllib} | 0 .../{PssmShadows.glsllib => Shadows.glsllib} | 96 ++- ...ssmShadows15.glsllib => Shadows15.glsllib} | 96 ++- .../com/jme3/shadow/AbstractShadowFilter.java | 233 ++++++ .../jme3/shadow/AbstractShadowRenderer.java | 563 +++++++++++++++ .../com/jme3/shadow/BasicShadowRenderer.java | 4 +- .../src/core/com/jme3/shadow/CompareMode.java | 48 ++ .../shadow/DirectionalLightShadowFilter.java | 174 +++++ .../DirectionalLightShadowRenderer.java | 257 +++++++ ...adowCamera.java => EdgeFilteringMode.java} | 73 +- .../jme3/shadow/PointLightShadowFilter.java | 178 +---- .../jme3/shadow/PointLightShadowRenderer.java | 668 +----------------- .../com/jme3/shadow/PssmShadowFilter.java | 2 + .../com/jme3/shadow/PssmShadowRenderer.java | 193 ++--- .../src/core/com/jme3/shadow/ShadowUtil.java | 10 +- .../jme3/shadow/SpotLightShadowFilter.java | 143 ++++ .../jme3/shadow/SpotLightShadowRenderer.java | 211 ++++++ .../jme3test/light/ShadowTestUIManager.java | 154 ++++ .../light/TestDirectionalLightShadow.java | 311 ++++++++ .../jme3test/light/TestPointLightShadows.java | 166 +---- .../test/jme3test/light/TestPssmShadow.java | 122 +++- .../jme3test/light/TestSpotLightShadows.java | 198 ++++++ 37 files changed, 3186 insertions(+), 1552 deletions(-) create mode 100644 engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.frag create mode 100644 engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.j3md create mode 100644 engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.vert create mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadow15.frag delete mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag delete mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md delete mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert delete mode 100644 engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag rename engine/src/core-data/Common/ShaderLib/{Shadow.glsllib => BasicShadow.glsllib} (100%) rename engine/src/core-data/Common/ShaderLib/{PssmShadows.glsllib => Shadows.glsllib} (58%) rename engine/src/core-data/Common/ShaderLib/{PssmShadows15.glsllib => Shadows15.glsllib} (60%) create mode 100644 engine/src/core/com/jme3/shadow/AbstractShadowFilter.java create mode 100644 engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java create mode 100644 engine/src/core/com/jme3/shadow/CompareMode.java create mode 100644 engine/src/core/com/jme3/shadow/DirectionalLightShadowFilter.java create mode 100644 engine/src/core/com/jme3/shadow/DirectionalLightShadowRenderer.java rename engine/src/core/com/jme3/shadow/{ShadowCamera.java => EdgeFilteringMode.java} (53%) create mode 100644 engine/src/core/com/jme3/shadow/SpotLightShadowFilter.java create mode 100644 engine/src/core/com/jme3/shadow/SpotLightShadowRenderer.java create mode 100644 engine/src/test/jme3test/light/ShadowTestUIManager.java create mode 100644 engine/src/test/jme3test/light/TestDirectionalLightShadow.java create mode 100644 engine/src/test/jme3test/light/TestSpotLightShadows.java diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md index 4b9703533..349c0ded4 100644 --- a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md +++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md @@ -200,8 +200,8 @@ MaterialDef Phong Lighting { Technique PostShadow15{ - VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM.vert - FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag + VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert + FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag WorldParameters { WorldViewProjectionMatrix @@ -228,8 +228,8 @@ MaterialDef Phong Lighting { } Technique PostShadow{ - VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.vert - FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag + VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert + FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag WorldParameters { WorldViewProjectionMatrix diff --git a/engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.frag b/engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.frag new file mode 100644 index 000000000..84bac8281 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.frag @@ -0,0 +1,12 @@ +#import "Common/ShaderLib/BasicShadow.glsllib" + +uniform SHADOWMAP m_ShadowMap; +varying vec4 projCoord; + +void main() { + vec4 coord = projCoord; + coord.xyz /= coord.w; + float shad = Shadow_GetShadow(m_ShadowMap, coord) * 0.7 + 0.3; + gl_FragColor = vec4(shad,shad,shad,1.0); +} + diff --git a/engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.j3md b/engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.j3md new file mode 100644 index 000000000..a07a23de4 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.j3md @@ -0,0 +1,28 @@ +MaterialDef Basic Post Shadow { + + MaterialParameters { + Texture2D ShadowMap + Matrix4 LightViewProjectionMatrix + } + + Technique { + VertexShader GLSL100: Common/MatDefs/Shadow/BasicPostShadow.vert + FragmentShader GLSL100: Common/MatDefs/Shadow/BasicPostShadow.frag + + WorldParameters { + WorldViewProjectionMatrix + WorldMatrix + } + + Defines { + NO_SHADOW2DPROJ + } + + RenderState { + Blend Modulate + DepthWrite Off + PolyOffset -0.1 0 + } + } + +} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.vert b/engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.vert new file mode 100644 index 000000000..3f09753f3 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.vert @@ -0,0 +1,31 @@ +uniform mat4 m_LightViewProjectionMatrix; +uniform mat4 g_WorldViewProjectionMatrix; +uniform mat4 g_WorldMatrix; + +varying vec4 projCoord; + +attribute vec3 inPosition; + +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); + +void main(){ + gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0); + + // get the vertex in world space + vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0); + + // convert vertex to light viewProj space + //projCoord = biasMat * (m_LightViewProjectionMatrix * worldPos); + vec4 coord = m_LightViewProjectionMatrix * worldPos; + projCoord = biasMat * coord; + //projCoord.z /= gl_DepthRange.far; + //projCoord = (m_LightViewProjectionMatrix * worldPos); + //projCoord /= projCoord.w; + //projCoord.xy = projCoord.xy * vec2(0.5, -0.5) + vec2(0.5); + + // bias from [-1, 1] to [0, 1] for sampling shadow map + //projCoord = (projCoord.xyzw * vec4(0.5)) + vec4(0.5); +} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag index 8c3d13344..06276fd0f 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag @@ -1,12 +1,72 @@ -#import "Common/ShaderLib/Shadow.glsllib" - -uniform SHADOWMAP m_ShadowMap; -varying vec4 projCoord; - -void main() { - vec4 coord = projCoord; - coord.xyz /= coord.w; - float shad = Shadow_GetShadow(m_ShadowMap, coord) * 0.7 + 0.3; - gl_FragColor = vec4(shad,shad,shad,1.0); -} - +#import "Common/ShaderLib/Shadows.glsllib" + +#ifdef PSSM +varying float shadowPosition; +#endif + +varying vec4 projCoord0; +varying vec4 projCoord1; +varying vec4 projCoord2; +varying vec4 projCoord3; + +#ifdef POINTLIGHT + varying vec4 projCoord4; + varying vec4 projCoord5; + uniform vec3 m_LightPos; + varying vec4 worldPos; +#endif + +#ifdef DISCARD_ALPHA + #ifdef COLOR_MAP + uniform sampler2D m_ColorMap; + #else + uniform sampler2D m_DiffuseMap; + #endif + uniform float m_AlphaDiscardThreshold; + varying vec2 texCoord; +#endif + +#ifdef FADE +uniform vec2 m_FadeInfo; +#endif + +void main(){ + + #ifdef DISCARD_ALPHA + #ifdef COLOR_MAP + float alpha = texture2D(m_ColorMap,texCoord).a; + #else + float alpha = texture2D(m_DiffuseMap,texCoord).a; + #endif + if(alpha<=m_AlphaDiscardThreshold){ + discard; + } + + #endif + + float shadow = 1.0; + + #ifdef POINTLIGHT + shadow = getPointLightShadows(worldPos, m_LightPos, + m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5, + projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5); + #else + #ifdef PSSM + shadow = getDirectionalLightShadows(m_Splits, shadowPosition, + m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3, + projCoord0, projCoord1, projCoord2, projCoord3); + #else + //spotlight + shadow = getSpotLightShadows(m_ShadowMap0,projCoord0); + #endif + #endif + + #ifdef FADE + shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); + #endif + 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/PostShadow.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md index e026f5edb..9ce31b654 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md @@ -1,8 +1,59 @@ MaterialDef Post Shadow { MaterialParameters { - Texture2D ShadowMap - Matrix4 LightViewProjectionMatrix + Int FilterMode + Boolean HardwareShadows + + Texture2D ShadowMap0 + Texture2D ShadowMap1 + Texture2D ShadowMap2 + Texture2D ShadowMap3 + //pointLights + Texture2D ShadowMap4 + Texture2D ShadowMap5 + + Float ShadowIntensity + Vector4 Splits + Vector2 FadeInfo + + Matrix4 LightViewProjectionMatrix0 + Matrix4 LightViewProjectionMatrix1 + Matrix4 LightViewProjectionMatrix2 + Matrix4 LightViewProjectionMatrix3 + //pointLight + Matrix4 LightViewProjectionMatrix4 + Matrix4 LightViewProjectionMatrix5 + Vector3 LightPos + + Float PCFEdge + + Float ShadowMapSize + } + + Technique { + VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert + FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag + + WorldParameters { + WorldViewProjectionMatrix + WorldMatrix + } + + Defines { + HARDWARE_SHADOWS : HardwareShadows + FILTER_MODE : FilterMode + PCFEDGE : PCFEdge + SHADOWMAP_SIZE : ShadowMapSize + FADE : FadeInfo + PSSM : Splits + POINTLIGHT : LightViewProjectionMatrix5 + } + + RenderState { + Blend Modulate + DepthWrite Off + PolyOffset -0.1 0 + } } Technique { @@ -15,7 +66,13 @@ MaterialDef Post Shadow { } Defines { - NO_SHADOW2DPROJ + HARDWARE_SHADOWS : HardwareShadows + FILTER_MODE : FilterMode + PCFEDGE : PCFEdge + SHADOWMAP_SIZE : ShadowMapSize + FADE : FadeInfo + PSSM : Splits + POINTLIGHT : LightViewProjectionMatrix5 } RenderState { diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert index 3f09753f3..f10f526c2 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert @@ -1,31 +1,72 @@ -uniform mat4 m_LightViewProjectionMatrix; +uniform mat4 m_LightViewProjectionMatrix0; +uniform mat4 m_LightViewProjectionMatrix1; +uniform mat4 m_LightViewProjectionMatrix2; +uniform mat4 m_LightViewProjectionMatrix3; + uniform mat4 g_WorldViewProjectionMatrix; uniform mat4 g_WorldMatrix; +uniform mat4 g_ViewMatrix; +uniform vec3 m_LightPos; + +varying vec4 projCoord0; +varying vec4 projCoord1; +varying vec4 projCoord2; +varying vec4 projCoord3; + +#ifdef POINTLIGHT +uniform mat4 m_LightViewProjectionMatrix4; +uniform mat4 m_LightViewProjectionMatrix5; +varying vec4 projCoord4; +varying vec4 projCoord5; +varying vec4 worldPos; +#endif + +#ifdef PSSM +varying float shadowPosition; +#endif +varying vec3 lightVec; -varying vec4 projCoord; +varying vec2 texCoord; attribute vec3 inPosition; +#ifdef DISCARD_ALPHA + attribute vec2 inTexCoord; +#endif + const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0); + void main(){ gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0); + #ifndef POINTLIGHT + #ifdef PSSM + shadowPosition = gl_Position.z; + #endif + vec4 worldPos=vec4(0.0); + #endif // get the vertex in world space - vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0); - - // convert vertex to light viewProj space - //projCoord = biasMat * (m_LightViewProjectionMatrix * worldPos); - vec4 coord = m_LightViewProjectionMatrix * worldPos; - projCoord = biasMat * coord; - //projCoord.z /= gl_DepthRange.far; - //projCoord = (m_LightViewProjectionMatrix * worldPos); - //projCoord /= projCoord.w; - //projCoord.xy = projCoord.xy * vec2(0.5, -0.5) + vec2(0.5); - - // bias from [-1, 1] to [0, 1] for sampling shadow map - //projCoord = (projCoord.xyzw * vec4(0.5)) + vec4(0.5); + worldPos = g_WorldMatrix * vec4(inPosition, 1.0); + + #ifdef DISCARD_ALPHA + texCoord = inTexCoord; + #endif + // populate the light view matrices array and convert vertex to light viewProj space + projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos; + projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; + projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos; + projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos; + #ifdef POINTLIGHT + projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos; + projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos; + #else + + vec4 vLightPos = g_ViewMatrix * vec4(m_LightPos,1.0); + vec4 vPos = g_ViewMatrix * worldPos; + lightVec = vLightPos.xyz - vPos.xyz; + #endif } \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadow15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow15.frag new file mode 100644 index 000000000..255610e11 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadow15.frag @@ -0,0 +1,72 @@ +#import "Common/ShaderLib/Shadows15.glsllib" + +out vec4 outFragColor; + +#ifdef PSSM +in float shadowPosition; +#endif + +in vec4 projCoord0; +in vec4 projCoord1; +in vec4 projCoord2; +in vec4 projCoord3; + +#ifdef POINTLIGHT + in vec4 projCoord4; + in vec4 projCoord5; + in vec4 worldPos; + uniform vec3 m_LightPos; +#endif + +#ifdef DISCARD_ALPHA + #ifdef COLOR_MAP + uniform sampler2D m_ColorMap; + #else + uniform sampler2D m_DiffuseMap; + #endif + uniform float m_AlphaDiscardThreshold; + varying vec2 texCoord; +#endif + +#ifdef FADE +uniform vec2 m_FadeInfo; +#endif + +void main(){ + + #ifdef DISCARD_ALPHA + #ifdef COLOR_MAP + float alpha = texture2D(m_ColorMap,texCoord).a; + #else + float alpha = texture2D(m_DiffuseMap,texCoord).a; + #endif + + if(alpha < m_AlphaDiscardThreshold){ + discard; + } + #endif + + float shadow = 1.0; + #ifdef POINTLIGHT + shadow = getPointLightShadows(worldPos, m_LightPos, + m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5, + projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5); + #else + #ifdef PSSM + shadow = getDirectionalLightShadows(m_Splits, shadowPosition, + m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3, + projCoord0, projCoord1, projCoord2, projCoord3); + #else + //spotlight + shadow = getSpotLightShadows(m_ShadowMap0,projCoord0); + #endif + #endif + + #ifdef FADE + shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); + #endif + + shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); + outFragColor = vec4(shadow, shadow, shadow, 1.0); +} + diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag index 6111f8979..c2666fcc8 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag @@ -1,4 +1,4 @@ -#import "Common/ShaderLib/PssmShadows.glsllib" +#import "Common/ShaderLib/Shadows.glsllib" uniform sampler2D m_Texture; uniform sampler2D m_DepthTexture; @@ -59,46 +59,21 @@ void main(){ #endif float shadow = 1.0; - #ifdef PSSM - float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w; - - 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); - } - #endif - + #ifdef POINTLIGHT - vec3 vect = worldPos.xyz - m_LightPos; - vec3 absv= abs(vect); - float maxComp = max(absv.x,max(absv.y,absv.z)); - if(maxComp == absv.y){ - if(vect.y < 0.0){ - shadow = GETSHADOW(m_ShadowMap0, projCoord0); - }else{ - shadow = GETSHADOW(m_ShadowMap1, projCoord1); - } - }else if(maxComp == absv.z){ - if(vect.z < 0.0){ - shadow = GETSHADOW(m_ShadowMap2, projCoord2); - }else{ - shadow = GETSHADOW(m_ShadowMap3, projCoord3); - } - }else if(maxComp == absv.x){ - if(vect.x < 0.0){ - shadow = GETSHADOW(m_ShadowMap4, projCoord4); - }else{ - shadow = GETSHADOW(m_ShadowMap5, projCoord5); - } - } + shadow = getPointLightShadows(worldPos, m_LightPos, + m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5, + projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5); + #else + #ifdef PSSM + float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w; + shadow = getDirectionalLightShadows(m_Splits, shadowPosition, + m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3, + projCoord0, projCoord1, projCoord2, projCoord3); + #else + //spotlight + shadow = getSpotLightShadows(m_ShadowMap0,projCoord0); + #endif #endif #ifdef FADE diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md index 356d353db..b3aa35ac9 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md @@ -11,8 +11,7 @@ MaterialDef Post Shadow { //pointLights Texture2D ShadowMap4 Texture2D ShadowMap5 - Vector3 LightPos - + Float ShadowIntensity Vector4 Splits Vector2 FadeInfo @@ -23,7 +22,8 @@ MaterialDef Post Shadow { Matrix4 LightViewProjectionMatrix3 //pointLight Matrix4 LightViewProjectionMatrix4 - Matrix4 LightViewProjectionMatrix5 + Matrix4 LightViewProjectionMatrix5 + Vector3 LightPos Float PCFEdge @@ -44,7 +44,7 @@ MaterialDef Post Shadow { FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag WorldParameters { - WorldViewProjectionMatrix + WorldViewProjectionMatrix } Defines { diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag index edae776f7..a8280ea1c 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag @@ -1,5 +1,5 @@ #import "Common/ShaderLib/MultiSample.glsllib" -#import "Common/ShaderLib/PssmShadows15.glsllib" +#import "Common/ShaderLib/Shadows15.glsllib" uniform COLORTEXTURE m_Texture; @@ -59,53 +59,29 @@ vec4 main_multiSample(in int numSample){ #endif float shadow = 1.0; - #ifdef PSSM - float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w; - - 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); - } - #endif - + #ifdef POINTLIGHT - vec3 vect = worldPos.xyz - m_LightPos; - vec3 absv= abs(vect); - float maxComp = max(absv.x,max(absv.y,absv.z)); - if(maxComp == absv.y){ - if(vect.y < 0.0){ - shadow = GETSHADOW(m_ShadowMap0, projCoord0); - }else{ - shadow = GETSHADOW(m_ShadowMap1, projCoord1); - } - }else if(maxComp == absv.z){ - if(vect.z < 0.0){ - shadow = GETSHADOW(m_ShadowMap2, projCoord2); - }else{ - shadow = GETSHADOW(m_ShadowMap3, projCoord3); - } - }else if(maxComp == absv.x){ - if(vect.x < 0.0){ - shadow = GETSHADOW(m_ShadowMap4, projCoord4); - }else{ - shadow = GETSHADOW(m_ShadowMap5, projCoord5); - } - } + shadow = getPointLightShadows(worldPos, m_LightPos, + m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5, + projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5); + #else + #ifdef PSSM + float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w; + shadow = getDirectionalLightShadows(m_Splits, shadowPosition, + m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3, + projCoord0, projCoord1, projCoord2, projCoord3); + #else + //spotlight + shadow = getSpotLightShadows(m_ShadowMap0,projCoord0); + #endif #endif + #ifdef FADE shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); #endif + shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); - return color * vec4(shadow, shadow, shadow, 1.0); } diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag deleted file mode 100644 index 82097b93f..000000000 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag +++ /dev/null @@ -1,96 +0,0 @@ -#import "Common/ShaderLib/PssmShadows.glsllib" - -#ifdef PSSM -varying float shadowPosition; -#endif - -varying vec4 projCoord0; -varying vec4 projCoord1; -varying vec4 projCoord2; -varying vec4 projCoord3; - -#ifdef POINTLIGHT - varying vec4 projCoord4; - varying vec4 projCoord5; - uniform vec3 m_LightPos; - varying vec4 worldPos; -#endif - -#ifdef DISCARD_ALPHA - #ifdef COLOR_MAP - uniform sampler2D m_ColorMap; - #else - uniform sampler2D m_DiffuseMap; - #endif - uniform float m_AlphaDiscardThreshold; - varying vec2 texCoord; -#endif - -#ifdef FADE -uniform vec2 m_FadeInfo; -#endif - -void main(){ - - #ifdef DISCARD_ALPHA - #ifdef COLOR_MAP - float alpha = texture2D(m_ColorMap,texCoord).a; - #else - float alpha = texture2D(m_DiffuseMap,texCoord).a; - #endif - if(alpha<=m_AlphaDiscardThreshold){ - discard; - } - - #endif - - float shadow = 1.0; - #ifdef PSSM - 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); - } - #endif - - #ifdef POINTLIGHT - vec3 vect = worldPos.xyz - m_LightPos; - vec3 absv= abs(vect); - float maxComp = max(absv.x,max(absv.y,absv.z)); - if(maxComp == absv.y){ - if(vect.y < 0.0){ - shadow = GETSHADOW(m_ShadowMap0, projCoord0); - }else{ - shadow = GETSHADOW(m_ShadowMap1, projCoord1); - } - }else if(maxComp == absv.z){ - if(vect.z < 0.0){ - shadow = GETSHADOW(m_ShadowMap2, projCoord2); - }else{ - shadow = GETSHADOW(m_ShadowMap3, projCoord3); - } - }else if(maxComp == absv.x){ - if(vect.x < 0.0){ - shadow = GETSHADOW(m_ShadowMap4, projCoord4); - }else{ - shadow = GETSHADOW(m_ShadowMap5, projCoord5); - } - } - #endif - - #ifdef FADE - shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); - #endif - 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/PostShadowPSSM.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md deleted file mode 100644 index 60bf7e3c6..000000000 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md +++ /dev/null @@ -1,85 +0,0 @@ -MaterialDef Post Shadow { - - MaterialParameters { - Int FilterMode - Boolean HardwareShadows - - Texture2D ShadowMap0 - Texture2D ShadowMap1 - Texture2D ShadowMap2 - Texture2D ShadowMap3 - //pointLights - Texture2D ShadowMap4 - Texture2D ShadowMap5 - - Float ShadowIntensity - Vector4 Splits - Vector2 FadeInfo - - Matrix4 LightViewProjectionMatrix0 - Matrix4 LightViewProjectionMatrix1 - Matrix4 LightViewProjectionMatrix2 - Matrix4 LightViewProjectionMatrix3 - //pointLight - Matrix4 LightViewProjectionMatrix4 - Matrix4 LightViewProjectionMatrix5 - Vector3 LightPos - - Float PCFEdge - - Float ShadowMapSize - } - - Technique { - VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM.vert - FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag - - WorldParameters { - WorldViewProjectionMatrix - WorldMatrix - } - - Defines { - HARDWARE_SHADOWS : HardwareShadows - FILTER_MODE : FilterMode - PCFEDGE : PCFEdge - SHADOWMAP_SIZE : ShadowMapSize - FADE : FadeInfo - PSSM : Splits - POINTLIGHT : LightViewProjectionMatrix5 - } - - RenderState { - Blend Modulate - DepthWrite Off - PolyOffset -0.1 0 - } - } - - Technique { - VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.vert - FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag - - WorldParameters { - WorldViewProjectionMatrix - WorldMatrix - } - - Defines { - HARDWARE_SHADOWS : HardwareShadows - FILTER_MODE : FilterMode - PCFEDGE : PCFEdge - SHADOWMAP_SIZE : ShadowMapSize - FADE : FadeInfo - PSSM : Splits - POINTLIGHT : LightViewProjectionMatrix5 - } - - RenderState { - Blend Modulate - DepthWrite Off - PolyOffset -0.1 0 - } - } - -} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert deleted file mode 100644 index 2a67c5e2b..000000000 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert +++ /dev/null @@ -1,62 +0,0 @@ -uniform mat4 m_LightViewProjectionMatrix0; -uniform mat4 m_LightViewProjectionMatrix1; -uniform mat4 m_LightViewProjectionMatrix2; -uniform mat4 m_LightViewProjectionMatrix3; - -uniform mat4 g_WorldViewProjectionMatrix; -uniform mat4 g_WorldMatrix; - -varying vec4 projCoord0; -varying vec4 projCoord1; -varying vec4 projCoord2; -varying vec4 projCoord3; - -#ifdef POINTLIGHT -uniform mat4 m_LightViewProjectionMatrix4; -uniform mat4 m_LightViewProjectionMatrix5; -varying vec4 projCoord4; -varying vec4 projCoord5; -varying vec4 worldPos; -#endif - -#ifdef PSSM -varying float shadowPosition; -#endif - -varying vec2 texCoord; - -attribute vec3 inPosition; - -#ifdef DISCARD_ALPHA - attribute vec2 inTexCoord; -#endif - -const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 0.0, - 0.0, 0.0, 0.5, 0.0, - 0.5, 0.5, 0.5, 1.0); - - -void main(){ - gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0); - - #ifdef PSSM - shadowPosition = gl_Position.z; - vec4 worldPos=vec4(0.0); - #endif - // get the vertex in world space - worldPos = g_WorldMatrix * vec4(inPosition, 1.0); - - #ifdef DISCARD_ALPHA - texCoord = inTexCoord; - #endif - // populate the light view matrices array and convert vertex to light viewProj space - projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos; - projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; - projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos; - projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos; - #ifdef POINTLIGHT - projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos; - projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos; - #endif -} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag deleted file mode 100644 index b97bea408..000000000 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag +++ /dev/null @@ -1,105 +0,0 @@ -#import "Common/ShaderLib/PssmShadows15.glsllib" - -out vec4 outFragColor; - -#ifdef PSSM -in float shadowPosition; -#endif - -in vec4 projCoord0; -in vec4 projCoord1; -in vec4 projCoord2; -in vec4 projCoord3; - -#ifdef POINTLIGHT - in vec4 projCoord4; - in vec4 projCoord5; - uniform vec3 m_LightPos; - in vec4 worldPos; -#endif - -#ifdef DISCARD_ALPHA - #ifdef COLOR_MAP - uniform sampler2D m_ColorMap; - #else - uniform sampler2D m_DiffuseMap; - #endif - uniform float m_AlphaDiscardThreshold; - varying vec2 texCoord; -#endif - -#ifdef FADE -uniform vec2 m_FadeInfo; -#endif - -void main(){ - - #ifdef DISCARD_ALPHA - #ifdef COLOR_MAP - float alpha = texture2D(m_ColorMap,texCoord).a; - #else - float alpha = texture2D(m_DiffuseMap,texCoord).a; - #endif - - if(alpha < m_AlphaDiscardThreshold){ - discard; - } - #endif - - float shadow = 1.0; - #ifdef PSSM - 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); - } - #endif - - - #ifdef POINTLIGHT - vec3 vect = worldPos.xyz - m_LightPos; - vec3 absv= abs(vect); - float maxComp = max(absv.x,max(absv.y,absv.z)); - if(maxComp == absv.y){ - if(vect.y < 0.0){ - shadow = GETSHADOW(m_ShadowMap0, projCoord0); -outFragColor = vec4(projCoord0.z); - }else{ - shadow = GETSHADOW(m_ShadowMap1, projCoord1); -outFragColor = vec4(projCoord1.z); - } - }else if(maxComp == absv.z){ - if(vect.z < 0.0){ - shadow = GETSHADOW(m_ShadowMap2, projCoord2); -outFragColor =vec4(projCoord2.z); - }else{ - shadow = GETSHADOW(m_ShadowMap3, projCoord3); -outFragColor = vec4(projCoord3.z); - } - }else if(maxComp == absv.x){ - if(vect.x < 0.0){ - shadow = GETSHADOW(m_ShadowMap4, projCoord4); -outFragColor = vec4(projCoord4.z); - }else{ - shadow = GETSHADOW(m_ShadowMap5, projCoord5); -outFragColor = vec4(projCoord5.z); - } - } - #endif - - #ifdef FADE - shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); - #endif - - shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); - outFragColor = vec4(shadow, shadow, shadow, 1.0); - -} - diff --git a/engine/src/core-data/Common/ShaderLib/Shadow.glsllib b/engine/src/core-data/Common/ShaderLib/BasicShadow.glsllib similarity index 100% rename from engine/src/core-data/Common/ShaderLib/Shadow.glsllib rename to engine/src/core-data/Common/ShaderLib/BasicShadow.glsllib diff --git a/engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib b/engine/src/core-data/Common/ShaderLib/Shadows.glsllib similarity index 58% rename from engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib rename to engine/src/core-data/Common/ShaderLib/Shadows.glsllib index 9ead707ab..0a0d8ee42 100644 --- a/engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib +++ b/engine/src/core-data/Common/ShaderLib/Shadows.glsllib @@ -3,7 +3,7 @@ #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r #else #define SHADOWMAP sampler2D - #define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, texture2DProj(tex, coord).r) + #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r) #endif #if FILTER_MODE == 0 @@ -49,29 +49,31 @@ 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){ +float Shadow_DoShadowCompareOffset(SHADOWMAP tex, vec4 projCoord, 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){ +float Shadow_DoShadowCompare(SHADOWMAP tex, vec4 projCoord){ return SHADOWCOMPARE(tex, projCoord); } -float Shadow_BorderCheck(in vec2 coord){ -#ifdef PSSM +float Shadow_BorderCheck(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); -#else -//fix me this is a big fat hack to avoid issues whith point lights, -//this function fail to return correct values, but it works with PSSM - return 0.0; -#endif } -float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ +float Shadow_Nearest(SHADOWMAP tex, vec4 projCoord){ + float border = Shadow_BorderCheck(projCoord.xy); + if (border > 0.0){ + return 1.0; + } + return Shadow_DoShadowCompare(tex,projCoord); +} + +float Shadow_DoDither_2x2(SHADOWMAP tex, vec4 projCoord){ float border = Shadow_BorderCheck(projCoord.xy); if (border > 0.0) return 1.0; @@ -87,7 +89,7 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ return shadow; } -float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ +float Shadow_DoBilinear_2x2(SHADOWMAP tex, vec4 projCoord){ float border = Shadow_BorderCheck(projCoord.xy); if (border > 0.0) return 1.0; @@ -102,7 +104,7 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ return mix( mx.x, mx.y, f.y ); } -float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ +float Shadow_DoPCF(SHADOWMAP tex, vec4 projCoord){ float shadow = 0.0; float border = Shadow_BorderCheck(projCoord.xy); if (border > 0.0) @@ -136,7 +138,7 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ 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_DoPCFPoisson(SHADOWMAP tex, vec4 projCoord){ float shadow = 0.0; float border = Shadow_BorderCheck(projCoord.xy); if (border > 0.0) @@ -161,3 +163,69 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){ return shadow; } + +#ifdef POINTLIGHT + float getPointLightShadows(vec4 worldPos,vec3 lightPos, + SHADOWMAP shadowMap0,SHADOWMAP shadowMap1,SHADOWMAP shadowMap2,SHADOWMAP shadowMap3,SHADOWMAP shadowMap4,SHADOWMAP shadowMap5, + vec4 projCoord0,vec4 projCoord1,vec4 projCoord2,vec4 projCoord3,vec4 projCoord4,vec4 projCoord5){ + float shadow = 1.0; + vec3 vect = worldPos.xyz - lightPos; + vec3 absv= abs(vect); + float maxComp = max(absv.x,max(absv.y,absv.z)); + if(maxComp == absv.y){ + if(vect.y < 0.0){ + shadow = GETSHADOW(shadowMap0, projCoord0 / projCoord0.w); + }else{ + shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w); + } + }else if(maxComp == absv.z){ + if(vect.z < 0.0){ + shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w); + }else{ + shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w); + } + }else if(maxComp == absv.x){ + if(vect.x < 0.0){ + shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w); + }else{ + shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w); + } + } + return shadow; + } +#else + #ifdef PSSM + float getDirectionalLightShadows(vec4 splits,float shadowPosition, + SHADOWMAP shadowMap0,SHADOWMAP shadowMap1,SHADOWMAP shadowMap2,SHADOWMAP shadowMap3, + vec4 projCoord0,vec4 projCoord1,vec4 projCoord2,vec4 projCoord3){ + float shadow = 1.0; + if(shadowPosition < splits.x){ + shadow = GETSHADOW(shadowMap0, projCoord0 ); + }else if( shadowPosition < splits.y){ + shadowBorderScale = 0.5; + shadow = GETSHADOW(shadowMap1, projCoord1); + }else if( shadowPosition < splits.z){ + shadowBorderScale = 0.25; + shadow = GETSHADOW(shadowMap2, projCoord2); + }else if( shadowPosition < splits.w){ + shadowBorderScale = 0.125; + shadow = GETSHADOW(shadowMap3, projCoord3); + } + return shadow; + } + #else + float getSpotLightShadows(SHADOWMAP shadowMap, vec4 projCoord){ + float shadow = 1.0; + projCoord /= projCoord.w; + shadow = GETSHADOW(shadowMap, projCoord); + + //a small falloff to make the shadow blend nicely into the not lighten + //we translate the texture coordinate value to a -1,1 range so the length + //of the texture coordinate vector is actually the radius of the lighten area on the ground + projCoord = projCoord * 2.0 - 1.0; + float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1; + return mix(shadow,1.0,clamp(fallOff,0.0,1.0)); + + } + #endif +#endif diff --git a/engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib b/engine/src/core-data/Common/ShaderLib/Shadows15.glsllib similarity index 60% rename from engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib rename to engine/src/core-data/Common/ShaderLib/Shadows15.glsllib index 6dd071229..baaf20441 100644 --- a/engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib +++ b/engine/src/core-data/Common/ShaderLib/Shadows15.glsllib @@ -9,18 +9,18 @@ #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z) #else #define SHADOWMAP sampler2D - #define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z / coord.w, textureProjOffset(tex, coord, offset).r) - #define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, textureProj(tex, coord).r) + #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 GETSHADOW Shadow_Nearest #define KERNEL 1 #elif FILTER_MODE == 1 #ifdef HARDWARE_SHADOWS - #define GETSHADOW SHADOWCOMPARE + #define GETSHADOW Shadow_Nearest #else #define GETSHADOW Shadow_DoBilinear_2x2 #endif @@ -59,16 +59,18 @@ const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE); float shadowBorderScale = 1.0; float Shadow_BorderCheck(in vec2 coord){ -#ifdef PSSM // 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); -#else -//fix me this is a big fat hack to avoid issues whith point lights, -//this function fail to return correct values, but it works with PSSM - return 0.0; -#endif + return dot(t,t); +} + +float Shadow_Nearest(in SHADOWMAP tex, in vec4 projCoord){ + float border = Shadow_BorderCheck(projCoord.xy); + if (border > 0.0){ + return 1.0; + } + return SHADOWCOMPARE(tex,projCoord); } float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ @@ -109,9 +111,9 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ return mix( mx.x, mx.y, f.y ); } -float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ - - vec2 pixSize = pixSize2 * shadowBorderScale; +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) @@ -146,7 +148,7 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419); -float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){ +float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){ float shadow = 0.0; float border = Shadow_BorderCheck(projCoord.xy); @@ -173,4 +175,68 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){ return shadow * 0.08333333333; } +#ifdef POINTLIGHT + float getPointLightShadows(in vec4 worldPos,in vec3 lightPos, + in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,in SHADOWMAP shadowMap4,in SHADOWMAP shadowMap5, + in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3,in vec4 projCoord4,in vec4 projCoord5){ + float shadow = 1.0; + vec3 vect = worldPos.xyz - lightPos; + vec3 absv= abs(vect); + float maxComp = max(absv.x,max(absv.y,absv.z)); + if(maxComp == absv.y){ + if(vect.y < 0.0){ + shadow = GETSHADOW(shadowMap0, projCoord0 / projCoord0.w); + }else{ + shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w); + } + }else if(maxComp == absv.z){ + if(vect.z < 0.0){ + shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w); + }else{ + shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w); + } + }else if(maxComp == absv.x){ + if(vect.x < 0.0){ + shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w); + }else{ + shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w); + } + } + return shadow; + } +#else + #ifdef PSSM + float getDirectionalLightShadows(in vec4 splits,in float shadowPosition, + in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3, + in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3){ + float shadow = 1.0; + if(shadowPosition < splits.x){ + shadow = GETSHADOW(shadowMap0, projCoord0 ); + }else if( shadowPosition < splits.y){ + shadowBorderScale = 0.5; + shadow = GETSHADOW(shadowMap1, projCoord1); + }else if( shadowPosition < splits.z){ + shadowBorderScale = 0.25; + shadow = GETSHADOW(shadowMap2, projCoord2); + }else if( shadowPosition < splits.w){ + shadowBorderScale = 0.125; + shadow = GETSHADOW(shadowMap3, projCoord3); + } + return shadow; + } + #else + float getSpotLightShadows(in SHADOWMAP shadowMap,in vec4 projCoord){ + float shadow = 1.0; + projCoord /= projCoord.w; + shadow = GETSHADOW(shadowMap,projCoord); + + //a small falloff to make the shadow blend nicely into the not lighten + //we translate the texture coordinate value to a -1,1 range so the length + //of the texture coordinate vector is actually the radius of the lighten area on the ground + projCoord = projCoord * 2.0 - 1.0; + float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1; + return mix(shadow,1.0,clamp(fallOff,0.0,1.0)); + } + #endif +#endif diff --git a/engine/src/core/com/jme3/shadow/AbstractShadowFilter.java b/engine/src/core/com/jme3/shadow/AbstractShadowFilter.java new file mode 100644 index 000000000..a10775458 --- /dev/null +++ b/engine/src/core/com/jme3/shadow/AbstractShadowFilter.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2009-2012 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.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.texture.FrameBuffer; +import java.io.IOException; + +/** + * + * Generic abstract filter that holds common implementations for the different + * shadow filtesr + * + * @author Rémy Bouquet aka Nehon + */ +public abstract class AbstractShadowFilter extends Filter { + + protected T shadowRenderer; + protected ViewPort viewPort; + + /** + * Abstract class constructor + * + * @param manager the application asset manager + * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, + * etc...) + * @param nbShadowMaps the number of shadow maps rendered (the more shadow + * maps the more quality, the less fps). + * @param shadowRenderer the shadowRenderer to use for this Filter + */ + @SuppressWarnings("all") + protected AbstractShadowFilter(AssetManager manager, int shadowMapSize, T shadowRenderer) { + super("Post Shadow"); + material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); + this.shadowRenderer = shadowRenderer; + this.shadowRenderer.setPostShadowMaterial(material); + } + + @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) { + shadowRenderer.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) { + shadowRenderer.postQueue(queue); + } + + @Override + protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { + shadowRenderer.setPostShadowParams(); + } + + @Override + protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { + shadowRenderer.needsfallBackMaterial = true; + shadowRenderer.initialize(renderManager, vp); + this.viewPort = vp; + } + + /** + * returns the shdaow intensity + * + * @see #setShadowIntensity(float shadowIntensity) + * @return shadowIntensity + */ + public float getShadowIntensity() { + return shadowRenderer.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) { + shadowRenderer.setShadowIntensity(shadowIntensity); + } + + /** + * returns the edges thickness
+ * + * @see #setEdgesThickness(int edgesThickness) + * @return edgesThickness + */ + public int getEdgesThickness() { + return shadowRenderer.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) { + shadowRenderer.setEdgesThickness(edgesThickness); + } + + /** + * returns true if the PssmRenderer flushed the shadow queues + * + * @return flushQueues + */ + public boolean isFlushQueues() { + return shadowRenderer.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) { + shadowRenderer.setFlushQueues(flushQueues); + } + + /** + * sets the shadow compare mode see {@link CompareMode} for more info + * + * @param compareMode + */ + final public void setShadowCompareMode(CompareMode compareMode) { + shadowRenderer.setShadowCompareMode(compareMode); + } + + /** + * returns the shadow compare mode + * + * @see CompareMode + * @return the shadowCompareMode + */ + public CompareMode getShadowCompareMode() { + return shadowRenderer.getShadowCompareMode(); + } + + /** + * Sets the filtering mode for shadow edges see {@link EdgeFilteringMode} + * for more info + * + * @param filterMode + */ + final public void setEdgeFilteringMode(EdgeFilteringMode filterMode) { + shadowRenderer.setEdgeFilteringMode(filterMode); + } + + /** + * returns the the edge filtering mode + * + * @see EdgeFilteringMode + * @return + */ + public EdgeFilteringMode getEdgeFilteringMode() { + return shadowRenderer.getEdgeFilteringMode(); + } + + @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/AbstractShadowRenderer.java b/engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java new file mode 100644 index 000000000..dbc3201e1 --- /dev/null +++ b/engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2009-2012 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.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Matrix4f; +import com.jme3.math.Vector3f; +import com.jme3.post.SceneProcessor; +import com.jme3.renderer.Camera; +import com.jme3.renderer.Caps; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.Renderer; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.GeometryList; +import com.jme3.renderer.queue.RenderQueue; +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.shadow.PssmShadowRenderer.FilterMode; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Image.Format; +import com.jme3.texture.Texture.MagFilter; +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; + +/** + * abstract shadow renderer that holds commons feature to have for a shadow renderer + * @author Rémy Bouquet aka Nehon + */ +public abstract class AbstractShadowRenderer implements SceneProcessor { + + protected int nbShadowMaps = 1; + protected float shadowMapSize; + protected float shadowIntensity = 0.7f; + protected RenderManager renderManager; + protected ViewPort viewPort; + protected FrameBuffer[] shadowFB; + protected Texture2D[] shadowMaps; + protected Texture2D dummyTex; + protected Material preshadowMat; + protected Material postshadowMat; + protected Matrix4f[] lightViewProjectionsMatrices; + protected boolean noOccluders = false; + protected AssetManager assetManager; + protected boolean debug = false; + protected float edgesThickness = 1.0f; + protected EdgeFilteringMode edgeFilteringMode; + protected CompareMode shadowCompareMode; + protected Picture[] dispPic; + protected boolean flushQueues = true; + // define if the fallback material should be used. + protected boolean needsfallBackMaterial = false; + //Name of the post material technique + protected String postTechniqueName = "PostShadow"; + //flags to know when to change params in the materials + //a list of material of the post shadow queue geometries. + protected List matCache = new ArrayList(); + + /** + * Create an abstract shadow renderer, this is to be called in extending classes + * @param assetManager the application asset manager + * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, + * etc...) + * @param nbShadowMaps the number of shadow maps rendered (the more shadow + * maps the more quality, the less fps). + */ + protected AbstractShadowRenderer(AssetManager assetManager, int shadowMapSize, int nbShadowMaps) { + + this.assetManager = assetManager; + this.postshadowMat = new Material(assetManager, "Common/MatDefs/Shadow/PostShadow.j3md"); + this.nbShadowMaps = nbShadowMaps; + this.shadowMapSize = shadowMapSize; + shadowFB = new FrameBuffer[nbShadowMaps]; + shadowMaps = new Texture2D[nbShadowMaps]; + dispPic = new Picture[nbShadowMaps]; + lightViewProjectionsMatrices = new Matrix4f[nbShadowMaps]; + + //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash) + dummyTex = new Texture2D(shadowMapSize, shadowMapSize, Format.RGBA8); + + preshadowMat = new Material(assetManager, "Common/MatDefs/Shadow/PreShadow.j3md"); + postshadowMat.setFloat("ShadowMapSize", shadowMapSize); + + for (int i = 0; i < nbShadowMaps; i++) { + lightViewProjectionsMatrices[i] = new Matrix4f(); + shadowFB[i] = new FrameBuffer(shadowMapSize, shadowMapSize, 1); + shadowMaps[i] = new Texture2D(shadowMapSize, shadowMapSize, Format.Depth); + + shadowFB[i].setDepthTexture(shadowMaps[i]); + + //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash) + shadowFB[i].setColorTexture(dummyTex); + + postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]); + + //quads for debuging purpose + dispPic[i] = new Picture("Picture" + i); + dispPic[i].setTexture(assetManager, shadowMaps[i], false); + } + + setShadowCompareMode(CompareMode.Hardware); + setEdgeFilteringMode(EdgeFilteringMode.Bilinear); + setShadowIntensity(0.7f); + + } + + /** + * set the post shadow material for this renderer + * @param postShadowMat + */ + protected final void setPostShadowMaterial(Material postShadowMat) { + this.postshadowMat = postShadowMat; + postshadowMat.setFloat("ShadowMapSize", shadowMapSize); + for (int i = 0; i < nbShadowMaps; i++) { + postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]); + } + setShadowCompareMode(shadowCompareMode); + setEdgeFilteringMode(edgeFilteringMode); + setShadowIntensity(shadowIntensity); + } + + /** + * Sets the filtering mode for shadow edges see {@link FilterMode} for more + * info + * + * @param filterMode + */ + final public void setEdgeFilteringMode(EdgeFilteringMode filterMode) { + if (filterMode == null) { + throw new NullPointerException(); + } + + if (this.edgeFilteringMode == filterMode) { + return; + } + + this.edgeFilteringMode = filterMode; + postshadowMat.setInt("FilterMode", filterMode.getMaterialParamValue()); + postshadowMat.setFloat("PCFEdge", edgesThickness); + if (shadowCompareMode == CompareMode.Hardware) { + for (Texture2D shadowMap : shadowMaps) { + if (filterMode == EdgeFilteringMode.Bilinear) { + shadowMap.setMagFilter(MagFilter.Bilinear); + shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps); + } else { + shadowMap.setMagFilter(MagFilter.Nearest); + shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); + } + } + } + } + + /** + * returns the the edge filtering mode + * + * @see EdgeFilteringMode + * @return + */ + public EdgeFilteringMode getEdgeFilteringMode() { + return edgeFilteringMode; + } + + /** + * sets the shadow compare mode see {@link CompareMode} for more info + * + * @param compareMode + */ + final public void setShadowCompareMode(CompareMode compareMode) { + if (compareMode == null) { + throw new NullPointerException(); + } + + if (this.shadowCompareMode == compareMode) { + return; + } + + this.shadowCompareMode = compareMode; + for (Texture2D shadowMap : shadowMaps) { + if (compareMode == CompareMode.Hardware) { + shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual); + if (edgeFilteringMode == EdgeFilteringMode.Bilinear) { + shadowMap.setMagFilter(MagFilter.Bilinear); + shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps); + } else { + shadowMap.setMagFilter(MagFilter.Nearest); + shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); + } + } else { + shadowMap.setShadowCompareMode(ShadowCompareMode.Off); + shadowMap.setMagFilter(MagFilter.Nearest); + shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); + } + } + postshadowMat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); + } + + /** + * returns the shadow compare mode + * + * @see CompareMode + * @return the shadowCompareMode + */ + public CompareMode getShadowCompareMode() { + return shadowCompareMode; + } + + //debug function that create a displayable frustrum + protected Geometry createFrustum(Vector3f[] pts, int i) { + WireFrustum frustum = new WireFrustum(pts); + Geometry frustumMdl = new Geometry("f", frustum); + frustumMdl.setCullHint(Spatial.CullHint.Never); + frustumMdl.setShadowMode(ShadowMode.Off); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.getAdditionalRenderState().setWireframe(true); + frustumMdl.setMaterial(mat); + switch (i) { + case 0: + frustumMdl.getMaterial().setColor("Color", ColorRGBA.Pink); + break; + case 1: + frustumMdl.getMaterial().setColor("Color", ColorRGBA.Red); + break; + case 2: + frustumMdl.getMaterial().setColor("Color", ColorRGBA.Green); + break; + case 3: + frustumMdl.getMaterial().setColor("Color", ColorRGBA.Blue); + break; + default: + frustumMdl.getMaterial().setColor("Color", ColorRGBA.White); + break; + } + + frustumMdl.updateGeometricState(); + return frustumMdl; + } + + public void initialize(RenderManager rm, ViewPort vp) { + renderManager = rm; + viewPort = vp; + //checking for caps to chosse the appropriate post material technique + if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) { + postTechniqueName = "PostShadow15"; + } else { + postTechniqueName = "PostShadow"; + } + } + + public boolean isInitialized() { + return viewPort != null; + } + + /** + * This mehtod is called once per frame. + * it is responsible for updating the shadow cams according to the light view. + * @param viewCam the scene cam + */ + protected abstract void updateShadowCams(Camera viewCam); + + /** + * this method must return the geomtryList that contains the oclluders to be rendered in the shadow map + * @param shadowMapIndex the index of the shadow map being rendered + * @param sceneOccluders the occluders of the whole scene + * @param sceneReceivers the recievers of the whole scene + * @return + */ + protected abstract GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers); + + /** + * return the shadow camera to use for rendering the shadow map according the given index + * @param shadowMapIndex the index of the shadow map being rendered + * @return the shadowCam + */ + protected abstract Camera getShadowCam(int shadowMapIndex); + + /** + * responsible for displaying the frustum of the shadow cam for debug purpose + * @param shadowMapIndex + */ + protected void doDisplayFrustumDebug(int shadowMapIndex) { + } + + @SuppressWarnings("fallthrough") + public void postQueue(RenderQueue rq) { + GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast); + GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive); + if (receivers.size() == 0 || occluders.size() == 0) { + return; + } + + updateShadowCams(viewPort.getCamera()); + + Renderer r = renderManager.getRenderer(); + renderManager.setForcedMaterial(preshadowMat); + renderManager.setForcedTechnique("PreShadow"); + + for (int shadowMapIndex = 0; shadowMapIndex < nbShadowMaps; shadowMapIndex++) { + + if (debugfrustums) { + doDisplayFrustumDebug(shadowMapIndex); + } + renderShadowMap(shadowMapIndex, occluders, receivers); + + } + + debugfrustums = false; + if (flushQueues) { + occluders.clear(); + } + //restore setting for future rendering + r.setFrameBuffer(viewPort.getOutputFrameBuffer()); + renderManager.setForcedMaterial(null); + renderManager.setForcedTechnique(null); + renderManager.setCamera(viewPort.getCamera(), false); + + } + + protected void renderShadowMap(int shadowMapIndex, GeometryList occluders, GeometryList receivers) { + GeometryList mapOccluders = getOccludersToRender(shadowMapIndex, occluders, receivers); + Camera shadowCam = getShadowCam(shadowMapIndex); + + //saving light view projection matrix for this split + lightViewProjectionsMatrices[shadowMapIndex].set(shadowCam.getViewProjectionMatrix()); + renderManager.setCamera(shadowCam, false); + + renderManager.getRenderer().setFrameBuffer(shadowFB[shadowMapIndex]); + renderManager.getRenderer().clearBuffers(false, true, false); + + // render shadow casters to shadow map + viewPort.getQueue().renderShadowQueue(mapOccluders, renderManager, shadowCam, true); + } + boolean debugfrustums = false; + + public void displayFrustum() { + debugfrustums = true; + } + + //debug only : displays depth shadow maps + 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].setWidth(128); + dispPic[i].setHeight(128); + dispPic[i].updateGeometricState(); + renderManager.renderGeometry(dispPic[i]); + } + renderManager.setCamera(cam, false); + } + + /** + * For dubuging purpose Allow to "snapshot" the current frustrum to the + * scene + */ + public void displayDebug() { + debug = true; + } + + public void postFrame(FrameBuffer out) { + + 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); + } + + //forcing the post shadow technique and render state + renderManager.setForcedTechnique(postTechniqueName); + + //rendering the post shadow pass + viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues); + + //resetting renderManager settings + renderManager.setForcedTechnique(null); + renderManager.setForcedMaterial(null); + renderManager.setCamera(cam, false); + + } + + } + + /** + * This method is called once per frame and is responsible of setting the material + * parameters than sub class may need to set on the post material + * @param material the materail to use for the post shadow pass + */ + protected abstract void setMaterialParameters(Material material); + + private void setMatParams() { + + GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive); + + //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 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) { + + mat.setFloat("ShadowMapSize", shadowMapSize); + + for (int j = 0; j < nbShadowMaps; j++) { + mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); + } + for (int j = 0; j < nbShadowMaps; j++) { + mat.setTexture("ShadowMap" + j, shadowMaps[j]); + } + mat.setBoolean("HardwareShadows", shadowCompareMode == CompareMode.Hardware); + mat.setInt("FilterMode", edgeFilteringMode.getMaterialParamValue()); + mat.setFloat("PCFEdge", edgesThickness); + mat.setFloat("ShadowIntensity", shadowIntensity); + + setMaterialParameters(mat); + } + + //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) { + setPostShadowParams(); + } + + } + + /** + * for internal use only + */ + protected void setPostShadowParams() { + setMaterialParameters(postshadowMat); + for (int j = 0; j < nbShadowMaps; j++) { + postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); + postshadowMat.setTexture("ShadowMap" + j, shadowMaps[j]); + } + } + + public void preFrame(float tpf) { + } + + public void cleanup() { + } + + public void reshape(ViewPort vp, int w, int h) { + } + + /** + * returns the shdaow intensity + * + * @see #setShadowIntensity(float shadowIntensity) + * @return shadowIntensity + */ + public float getShadowIntensity() { + return shadowIntensity; + } + + /** + * 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) { + this.shadowIntensity = shadowIntensity; + postshadowMat.setFloat("ShadowIntensity", shadowIntensity); + } + + /** + * returns the edges thickness + * + * @see #setEdgesThickness(int edgesThickness) + * @return edgesThickness + */ + public int getEdgesThickness() { + return (int) (edgesThickness * 10); + } + + /** + * 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) { + this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10)); + this.edgesThickness *= 0.1f; + postshadowMat.setFloat("PCFEdge", edgesThickness); + } + + /** + * returns true if the PssmRenderer flushed the shadow queues + * + * @return flushQueues + */ + public boolean isFlushQueues() { + return flushQueues; + } + + /** + * 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) { + this.flushQueues = flushQueues; + } +} diff --git a/engine/src/core/com/jme3/shadow/BasicShadowRenderer.java b/engine/src/core/com/jme3/shadow/BasicShadowRenderer.java index 1e1ec6c09..9e3b5a61a 100644 --- a/engine/src/core/com/jme3/shadow/BasicShadowRenderer.java +++ b/engine/src/core/com/jme3/shadow/BasicShadowRenderer.java @@ -52,7 +52,9 @@ import com.jme3.ui.Picture; * it's useful to render shadows in a small scene, but edges might look a bit jagged. * * @author Kirill Vainer + * @deprecated use {@link DirectionalLightShadowRenderer} with one split. */ +@Deprecated public class BasicShadowRenderer implements SceneProcessor { private RenderManager renderManager; @@ -79,7 +81,7 @@ public class BasicShadowRenderer implements SceneProcessor { shadowCam = new Camera(size, size); preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md"); - postshadowMat = new Material(manager, "Common/MatDefs/Shadow/PostShadow.j3md"); + postshadowMat = new Material(manager, "Common/MatDefs/Shadow/BasicPostShadow.j3md"); postshadowMat.setTexture("ShadowMap", shadowMap); dispPic.setTexture(manager, shadowMap, false); diff --git a/engine/src/core/com/jme3/shadow/CompareMode.java b/engine/src/core/com/jme3/shadow/CompareMode.java new file mode 100644 index 000000000..27e1bcc18 --- /dev/null +++ b/engine/src/core/com/jme3/shadow/CompareMode.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009-2012 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; + +/** + * Specifies the shadow comparison mode + */ +public enum CompareMode { + + /** + * Shadow depth comparisons are done by using shader code + */ + Software, + /** + * Shadow depth comparisons are done by using the GPU's dedicated shadowing + * pipeline. + */ + Hardware; +} diff --git a/engine/src/core/com/jme3/shadow/DirectionalLightShadowFilter.java b/engine/src/core/com/jme3/shadow/DirectionalLightShadowFilter.java new file mode 100644 index 000000000..e5bccf944 --- /dev/null +++ b/engine/src/core/com/jme3/shadow/DirectionalLightShadowFilter.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2009-2012 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.light.DirectionalLight; +import com.jme3.material.Material; +import java.io.IOException; + +/** + * + * This Filter does basically the same as a DirectionalLightShadowRenderer + * 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 DirectionalLightShadowFilter extends AbstractShadowFilter { + + + + /** + * Creates a DirectionalLightShadowFilter Shadow Filter More info on the + * technique at http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html + * + * @param assetManager the application asset manager + * @param shadowMapSize 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 DirectionalLightShadowFilter(AssetManager assetManager, int shadowMapSize, int nbSplits) { + super(assetManager, shadowMapSize, new DirectionalLightShadowRenderer(assetManager, shadowMapSize, nbSplits)); + } + + /** + * return the light used to cast shadows + * + * @return the DirectionalLight + */ + public DirectionalLight getLight() { + return shadowRenderer.getLight(); + } + + /** + * Sets the light to use to cast shadows + * + * @param light a DirectionalLight + */ + public void setLight(DirectionalLight light) { + shadowRenderer.setLight(light); + } + + /** + * returns the labda parameter + * + * @see #setLambda(float lambda) + * @return lambda + */ + public float getLambda() { + return shadowRenderer.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) { + shadowRenderer.setLambda(lambda); + } + + /** + * How far the shadows are rendered in the view + * + * @see setShadowZExtend(float zFar) + * @return shadowZExtend + */ + public float getShadowZExtend() { + return shadowRenderer.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) { + shadowRenderer.setShadowZExtend(zFar); + } + + /** + * Define the length over which the shadow will fade out when using a + * shadowZextend + * + * @param length the fade length in world units + */ + public void setShadowZFadeLength(float length) { + shadowRenderer.setShadowZFadeLength(length); + } + + /** + * get the length over which the shadow will fade out when using a + * shadowZextend + * + * @return the fade length in world units + */ + public float getShadowZFadeLength() { + return shadowRenderer.getShadowZFadeLength(); + } + + @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/DirectionalLightShadowRenderer.java b/engine/src/core/com/jme3/shadow/DirectionalLightShadowRenderer.java new file mode 100644 index 000000000..1e8330f7b --- /dev/null +++ b/engine/src/core/com/jme3/shadow/DirectionalLightShadowRenderer.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2009-2012 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.light.DirectionalLight; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.renderer.queue.GeometryList; +import com.jme3.renderer.queue.OpaqueComparator; +import com.jme3.scene.Node; + +/** + * DirectionalLightShadowRenderer renderer use Parrallel Split Shadow Mapping + * technique (pssm)
It splits the view frustum in several parts and compute + * a shadow map for each one.
splits are distributed so that the closer they + * are from the camera, the smaller they are to maximize the resolution used of + * the shadow map.
This result in a better quality shadow than standard + * shadow mapping.
for more informations on this read this http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
+ *

+ * @author Rémy Bouquet aka Nehon + */ +public class DirectionalLightShadowRenderer extends AbstractShadowRenderer { + + protected float lambda = 0.65f; + protected float zFarOverride = 0; + protected Camera shadowCam; + protected ColorRGBA splits; + protected GeometryList splitOccluders = new GeometryList(new OpaqueComparator()); + protected float[] splitsArray; + protected DirectionalLight light; + protected Vector3f[] points = new Vector3f[8]; + //Holding the info for fading shadows in the far distance + protected Vector2f fadeInfo; + protected float fadeLength; + + + /** + * Create a DirectionalLightShadowRenderer More info on the technique at http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html + * + * @param assetManager the application asset manager + * @param shadowMapSize 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 DirectionalLightShadowRenderer(AssetManager assetManager, int shadowMapSize, int nbSplits) { + super(assetManager, shadowMapSize, nbSplits); + + nbShadowMaps = Math.max(Math.min(nbSplits, 4), 1); + splits = new ColorRGBA(); + splitsArray = new float[nbSplits + 1]; + shadowCam = new Camera(shadowMapSize, shadowMapSize); + shadowCam.setParallelProjection(true); + + for (int i = 0; i < points.length; i++) { + points[i] = new Vector3f(); + } + } + + /** + * return the light used to cast shadows + * + * @return the DirectionalLight + */ + public DirectionalLight getLight() { + return light; + } + + /** + * Sets the light to use to cast shadows + * + * @param light a DirectionalLight + */ + public void setLight(DirectionalLight light) { + this.light = light; + } + + @Override + protected void updateShadowCams(Camera viewCam) { + + float zFar = zFarOverride; + if (zFar == 0) { + zFar = viewCam.getFrustumFar(); + } + + //We prevent computing the frustum points and splits with zeroed or negative near clip value + float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f); + ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points); + + //shadowCam.setDirection(direction); + shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp()); + shadowCam.update(); + shadowCam.updateViewProjection(); + + PssmShadowUtil.updateFrustumSplits(splitsArray, frustumNear, zFar, lambda); + + + switch (splitsArray.length) { + case 5: + splits.a = splitsArray[4]; + case 4: + splits.b = splitsArray[3]; + case 3: + splits.g = splitsArray[2]; + case 2: + case 1: + splits.r = splitsArray[1]; + break; + } + + } + + @Override + protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers) { + + // update frustum points based on current camera and split + ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], 1.0f, points); + + //Updating shadow cam with curent split frustra + ShadowUtil.updateShadowCamera(sceneOccluders, sceneReceivers, shadowCam, points, splitOccluders); + + return splitOccluders; + } + + @Override + protected Camera getShadowCam(int shadowMapIndex) { + return shadowCam; + } + + @Override + protected void doDisplayFrustumDebug(int shadowMapIndex) { + ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, shadowMapIndex)); + ShadowUtil.updateFrustumPoints2(shadowCam, points); + ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, shadowMapIndex)); + } + + @Override + protected void setMaterialParameters(Material material) { + material.setColor("Splits", splits); + } + + /** + * returns the labda parameter see #setLambda(float lambda) + * + * @return lambda + */ + public float getLambda() { + return lambda; + } + + /* + * 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) { + this.lambda = lambda; + } + + /** + * How far the shadows are rendered in the view + * + * @see #setShadowZExtend(float zFar) + * @return shadowZExtend + */ + public float getShadowZExtend() { + return zFarOverride; + } + + /** + * 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) { + if (fadeInfo != null) { + fadeInfo.set(zFar - fadeLength, 1f / fadeLength); + } + this.zFarOverride = zFar; + + } + + /** + * Define the length over which the shadow will fade out when using a + * shadowZextend This is useful to make dynamic shadows fade into baked + * shadows in the distance. + * + * @param length the fade length in world units + */ + public void setShadowZFadeLength(float length) { + if (length == 0) { + fadeInfo = null; + fadeLength = 0; + postshadowMat.clearParam("FadeInfo"); + } else { + if (zFarOverride == 0) { + fadeInfo = new Vector2f(0, 0); + } else { + fadeInfo = new Vector2f(zFarOverride - length, 1.0f / length); + } + fadeLength = length; + postshadowMat.setVector2("FadeInfo", fadeInfo); + } + } + + /** + * get the length over which the shadow will fade out when using a + * shadowZextend + * + * @return the fade length in world units + */ + public float getShadowZFadeLength() { + if (fadeInfo != null) { + return zFarOverride - fadeInfo.x; + } + return 0f; + } +} diff --git a/engine/src/core/com/jme3/shadow/ShadowCamera.java b/engine/src/core/com/jme3/shadow/EdgeFilteringMode.java similarity index 53% rename from engine/src/core/com/jme3/shadow/ShadowCamera.java rename to engine/src/core/com/jme3/shadow/EdgeFilteringMode.java index 9b72e534e..161e7684c 100644 --- a/engine/src/core/com/jme3/shadow/ShadowCamera.java +++ b/engine/src/core/com/jme3/shadow/EdgeFilteringMode.java @@ -31,45 +31,50 @@ */ package com.jme3.shadow; -import com.jme3.light.DirectionalLight; -import com.jme3.light.Light; -import com.jme3.light.PointLight; -import com.jme3.math.Vector3f; -import com.jme3.renderer.Camera; - /** - * Creates a camera according to a light - * Handy to compute projection matrix of a light - * @author Kirill Vainer + * ShadowEdgeFiltering specifies how shadows are filtered */ -public class ShadowCamera { +public enum EdgeFilteringMode { - private Vector3f[] points = new Vector3f[8]; - private Light target; + /** + * Shadows are not filtered. Nearest sample is used, causing in blocky + * shadows. + */ + Nearest(0), + /** + * Bilinear filtering is used. Has the potential of being hardware + * accelerated on some GPUs + */ + Bilinear(1), + /** + * Dither-based sampling is used, very cheap but can look bad at low + * resolutions. + */ + Dither(2), + /** + * 4x4 percentage-closer filtering is used. Shadows will be smoother at the + * cost of performance + */ + PCF4(3), + /** + * 8x8 percentage-closer filtering is used. Shadows will be smoother at the + * cost of performance + */ + PCFPOISSON(4), + /** + * 8x8 percentage-closer filtering is used. Shadows will be smoother at the + * cost of performance + */ + PCF8(5); + + int materialParamValue; - public ShadowCamera(Light target) { - this.target = target; - for (int i = 0; i < points.length; i++) { - points[i] = new Vector3f(); - } + private EdgeFilteringMode(int val) { + materialParamValue = val; } - /** - * Updates the camera view direction and position based on the light - */ - public void updateLightCamera(Camera lightCam) { - if (target.getType() == Light.Type.Directional) { - DirectionalLight dl = (DirectionalLight) target; - lightCam.setParallelProjection(true); - lightCam.setLocation(Vector3f.ZERO); - lightCam.lookAtDirection(dl.getDirection(), Vector3f.UNIT_Y); - lightCam.setFrustum(-1, 1, -1, 1, 1, -1); - } else { - PointLight pl = (PointLight) target; - lightCam.setParallelProjection(false); - lightCam.setLocation(pl.getPosition()); - // direction will have to be calculated automatically - lightCam.setFrustumPerspective(45, 1, 1, 300); - } + public int getMaterialParamValue() { + return materialParamValue; } + } diff --git a/engine/src/core/com/jme3/shadow/PointLightShadowFilter.java b/engine/src/core/com/jme3/shadow/PointLightShadowFilter.java index 966d03726..8c89bbe80 100644 --- a/engine/src/core/com/jme3/shadow/PointLightShadowFilter.java +++ b/engine/src/core/com/jme3/shadow/PointLightShadowFilter.java @@ -44,90 +44,44 @@ 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.PointLightShadowRenderer.CompareMode; -import com.jme3.shadow.PointLightShadowRenderer.FilterMode; import com.jme3.texture.FrameBuffer; import java.io.IOException; -/** - * - * This Filter does basically the same as a PointLightShadowRenderer except it renders - * the post shadow pass as a fulscreen quad pass instead of a geometry pass. - * It's mostly faster than PointLightShadowRenderer 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 - * +/** + * + * This Filter does basically the same as a PointLightShadowRenderer except it + * renders the post shadow pass as a fulscreen quad pass instead of a geometry + * pass. It's mostly faster than PointLightShadowRenderer 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 PointLightShadowFilter extends Filter { - - private PointLightShadowRenderer plRenderer; - private ViewPort viewPort; +public class PointLightShadowFilter extends AbstractShadowFilter { /** - * 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). + * Creates a PointLightShadowFilter + * + * @param assetManager the application asset manager + * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, + * etc...) */ - public PointLightShadowFilter(AssetManager manager, int size) { - super("Post Shadow"); - material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); - plRenderer = new PointLightShadowRenderer(manager, size, material); - plRenderer.needsfallBackMaterial = true; - } - - @Override - protected Material getMaterial() { - return material; + public PointLightShadowFilter(AssetManager assetManager, int shadowMapSize) { + super(assetManager, shadowMapSize,new PointLightShadowRenderer(assetManager, shadowMapSize)); } - @Override - protected boolean isRequiresDepthTexture() { - return true; - } - - public Material getShadowMaterial() { - return material; - } - Vector4f tmpv = new Vector4f(); - - @Override - protected void preFrame(float tpf) { - plRenderer.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) { - plRenderer.postQueue(queue); - } - - @Override - protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { - plRenderer.setPostShadowParams(); - } - - @Override - protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { - plRenderer.initialize(renderManager, vp); - this.viewPort = vp; - } - - /** + /** * gets the point light used to cast shadows with this processor * * @return the point light */ public PointLight getLight() { - return plRenderer.getLight(); + return shadowRenderer.getLight(); } /** @@ -136,93 +90,7 @@ public class PointLightShadowFilter extends Filter { * @param light the point light */ public void setLight(PointLight light) { - plRenderer.setLight(light); - } - - /** - * returns the shdaow intensity - * @see #setShadowIntensity(float shadowIntensity) - * @return shadowIntensity - */ - public float getShadowIntensity() { - return plRenderer.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) { - plRenderer.setShadowIntensity(shadowIntensity); - } - - /** - * returns the edges thickness
- * @see #setEdgesThickness(int edgesThickness) - * @return edgesThickness - */ - public int getEdgesThickness() { - return plRenderer.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) { - plRenderer.setEdgesThickness(edgesThickness); - } - - /** - * returns true if the PssmRenderer flushed the shadow queues - * @return flushQueues - */ - public boolean isFlushQueues() { - return plRenderer.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) { - plRenderer.setFlushQueues(flushQueues); - } - - /** - * sets the shadow compare mode see {@link CompareMode} for more info - * @param compareMode - */ - final public void setCompareMode(CompareMode compareMode) { - plRenderer.setCompareMode(compareMode); - } - - /** - * Sets the filtering mode for shadow edges see {@link FilterMode} for more info - * @param filterMode - */ - final public void setFilterMode(FilterMode filterMode) { - plRenderer.setFilterMode(filterMode); - } - - /** - * Define the length over which the shadow will fade out when using a shadowZextend - * @param length the fade length in world units - */ - public void setShadowZFadeLength(float length){ - plRenderer.setShadowZFadeLength(length); - } - - /** - * get the length over which the shadow will fade out when using a shadowZextend - * @return the fade length in world units - */ - public float getShadowZFadeLength(){ - return plRenderer.getShadowZFadeLength(); + shadowRenderer.setLight(light); } @Override diff --git a/engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java b/engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java index 331b7d9e8..e35827af9 100644 --- a/engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java +++ b/engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java @@ -34,451 +34,47 @@ package com.jme3.shadow; import com.jme3.asset.AssetManager; import com.jme3.light.PointLight; import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Matrix4f; -import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; -import com.jme3.post.SceneProcessor; import com.jme3.renderer.Camera; -import com.jme3.renderer.Caps; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.Renderer; -import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.GeometryList; import com.jme3.renderer.queue.OpaqueComparator; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.debug.WireFrustum; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.MagFilter; -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)
- * It splits the view frustum in several parts and compute a shadow map for each - * one.
splits are distributed so that the closer they are from the camera, - * the smaller they are to maximize the resolution used of the shadow map.
- * This result in a better quality shadow than standard shadow mapping.
for - * more informations on this read this http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
- *

+ * PointLightShadowRenderer renders shadows for a point light + * * @author Rémy Bouquet aka Nehon */ -public class PointLightShadowRenderer implements SceneProcessor { - - /** - * FilterMode specifies how shadows are filtered - */ - public enum FilterMode { +public class PointLightShadowRenderer extends AbstractShadowRenderer { - /** - * Shadows are not filtered. Nearest sample is used, causing in blocky - * shadows. - */ - Nearest, - /** - * Bilinear filtering is used. Has the potential of being hardware - * accelerated on some GPUs - */ - Bilinear, - /** - * Dither-based sampling is used, very cheap but can look bad at low - * resolutions. - */ - Dither, - /** - * 4x4 percentage-closer filtering is used. Shadows will be smoother at - * the cost of performance - */ - PCF4, - /** - * 8x8 percentage-closer filtering is used. Shadows will be smoother at - * the cost of performance - */ - PCFPOISSON, - /** - * 8x8 percentage-closer filtering is used. Shadows will be smoother at - * the cost of performance - */ - PCF8 - } - - /** - * Specifies the shadow comparison mode - */ - public enum CompareMode { - - /** - * Shadow depth comparisons are done by using shader code - */ - Software, - /** - * Shadow depth comparisons are done by using the GPU's dedicated - * shadowing pipeline. - */ - Hardware; - } - protected final int CAM_NUMBER = 6; + public static final int CAM_NUMBER = 6; protected PointLight light; - //common - protected float shadowMapSize; - protected float shadowIntensity = 0.7f; - protected float zFarOverride = 0; - protected RenderManager renderManager; - protected ViewPort viewPort; - protected FrameBuffer[] shadowFB; - protected Texture2D[] shadowMaps; - protected Texture2D dummyTex; protected Camera[] shadowCams; - protected Material preshadowMat; - protected Material postshadowMat; - protected GeometryList splitOccluders = new GeometryList(new OpaqueComparator()); - protected Matrix4f[] lightViewProjectionsMatrices; - protected boolean noOccluders = false; - protected AssetManager assetManager; - protected boolean debug = false; - protected float edgesThickness = 1.0f; - protected FilterMode filterMode; - protected CompareMode compareMode; - protected Picture[] dispPic; - protected boolean flushQueues = true; - // define if the fallback material should be used. - protected boolean needsfallBackMaterial = false; - //Name of the post material technique - protected String postTechniqueName = "PostShadow"; - //flags to know when to change params in the materials - protected boolean applyHWShadows = true; - protected boolean applyFilterMode = true; - protected boolean applyPCFEdge = true; - protected boolean applyShadowIntensity = true; - //a list of material of the post shadow queue geometries. - protected List matCache = new ArrayList(); - //Holding the info for fading shadows in the far distance - protected Vector2f fadeInfo; - protected float fadeLength; - protected boolean applyFadeInfo = false; - - /** - * Create a PSSM Shadow Renderer 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). - * @param nbSplits the number of shadow maps rendered (the more shadow maps - * the more quality, the less fps). - */ - public PointLightShadowRenderer(AssetManager manager, int size) { - this(manager, size, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md")); - } + protected GeometryList shadowMapOccluders = new GeometryList(new OpaqueComparator()); + private Geometry[] frustums = null; /** - * Create a PSSM Shadow Renderer More info on the technique at http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html + * Creates a PointLightShadowRenderer * - * @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 assetManager the application asset manager + * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, + * etc...) */ - protected PointLightShadowRenderer(AssetManager manager, int size, Material postShadowMat) { - - this.postshadowMat = postShadowMat; - assetManager = manager; - shadowMapSize = size; - - shadowFB = new FrameBuffer[CAM_NUMBER]; - shadowMaps = new Texture2D[CAM_NUMBER]; - dispPic = new Picture[CAM_NUMBER]; - lightViewProjectionsMatrices = new Matrix4f[CAM_NUMBER]; - - //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash) - dummyTex = new Texture2D(size, size, Format.RGBA8); - - preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md"); - postshadowMat.setFloat("ShadowMapSize", size); - + public PointLightShadowRenderer(AssetManager assetManager, int shadowMapSize) { + super(assetManager, shadowMapSize, CAM_NUMBER); shadowCams = new Camera[CAM_NUMBER]; - for (int i = 0; i < CAM_NUMBER; i++) { - lightViewProjectionsMatrices[i] = new Matrix4f(); - shadowFB[i] = new FrameBuffer(size, size, 1); - shadowMaps[i] = new Texture2D(size, size, Format.Depth); - - shadowFB[i].setDepthTexture(shadowMaps[i]); - - //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash) - shadowFB[i].setColorTexture(dummyTex); - - postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]); - - //quads for debuging purpose - dispPic[i] = new Picture("Picture" + i); - dispPic[i].setTexture(manager, shadowMaps[i], false); - - shadowCams[i] = new Camera(size, size); - } - - setCompareMode(CompareMode.Hardware); - setFilterMode(FilterMode.Bilinear); - setShadowIntensity(0.7f); - - } - - /** - * Sets the filtering mode for shadow edges see {@link FilterMode} for more - * info - * - * @param filterMode - */ - final public void setFilterMode(FilterMode filterMode) { - if (filterMode == null) { - throw new NullPointerException(); - } - - if (this.filterMode == filterMode) { - return; - } - - this.filterMode = filterMode; - postshadowMat.setInt("FilterMode", filterMode.ordinal()); - postshadowMat.setFloat("PCFEdge", edgesThickness); - if (compareMode == CompareMode.Hardware) { - for (Texture2D shadowMap : shadowMaps) { - if (filterMode == FilterMode.Bilinear) { - shadowMap.setMagFilter(MagFilter.Bilinear); - shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps); - } else { - shadowMap.setMagFilter(MagFilter.Nearest); - shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); - } - } - } - applyFilterMode = true; - } - - public FilterMode getFilterMode() { - return filterMode; - } - - /** - * sets the shadow compare mode see {@link CompareMode} for more info - * - * @param compareMode - */ - final public void setCompareMode(CompareMode compareMode) { - if (compareMode == null) { - throw new NullPointerException(); - } - - if (this.compareMode == compareMode) { - return; + shadowCams[i] = new Camera(shadowMapSize, shadowMapSize); } - - this.compareMode = compareMode; - for (Texture2D shadowMap : shadowMaps) { - if (compareMode == CompareMode.Hardware) { - shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual); - if (filterMode == FilterMode.Bilinear) { - shadowMap.setMagFilter(MagFilter.Bilinear); - shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps); - } else { - shadowMap.setMagFilter(MagFilter.Nearest); - shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); - } - } else { - shadowMap.setShadowCompareMode(ShadowCompareMode.Off); - shadowMap.setMagFilter(MagFilter.Nearest); - shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); - } - } - postshadowMat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); - applyHWShadows = true; } - //debug function that create a displayable frustrum - private Geometry createFrustum(Vector3f[] pts, int i) { - WireFrustum frustum = new WireFrustum(pts); - Geometry frustumMdl = new Geometry("f", frustum); - frustumMdl.setCullHint(Spatial.CullHint.Never); - frustumMdl.setShadowMode(ShadowMode.Off); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat.getAdditionalRenderState().setWireframe(true); - frustumMdl.setMaterial(mat); - switch (i) { - case 0: - frustumMdl.getMaterial().setColor("Color", ColorRGBA.Pink); - break; - case 1: - frustumMdl.getMaterial().setColor("Color", ColorRGBA.Red); - break; - case 2: - frustumMdl.getMaterial().setColor("Color", ColorRGBA.Green); - break; - case 3: - frustumMdl.getMaterial().setColor("Color", ColorRGBA.Blue); - break; - case 4: - frustumMdl.getMaterial().setColor("Color", ColorRGBA.Yellow); - break; - case 5: - frustumMdl.getMaterial().setColor("Color", ColorRGBA.Gray); - break; - default: - frustumMdl.getMaterial().setColor("Color", ColorRGBA.White); - break; - } + @Override + protected void updateShadowCams(Camera viewCam) { - frustumMdl.updateGeometricState(); - return frustumMdl; - } - - public void initialize(RenderManager rm, ViewPort vp) { - renderManager = rm; - viewPort = vp; - //checking for caps to chosse the appropriate post material technique - if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) { - postTechniqueName = "PostShadow15"; - } else { - postTechniqueName = "PostShadow"; - } - } - - public boolean isInitialized() { - return viewPort != null; - } - - @SuppressWarnings("fallthrough") - public void postQueue(RenderQueue rq) { if (light == null) { throw new IllegalStateException("The light can't be null for a " + this.getClass().getName()); } - GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast); - if (occluders.size() == 0) { - return; - } - - GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive); - if (receivers.size() == 0) { - return; - } - - Camera viewCam = viewPort.getCamera(); - - float zFar = zFarOverride; - if (zFar == 0) { - zFar = viewCam.getFrustumFar(); - } - - //We prevent computing the frustum points and splits with zeroed or negative near clip value - //float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f); - // ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points); - - updateShadowCams(); - - Renderer r = renderManager.getRenderer(); - renderManager.setForcedMaterial(preshadowMat); - renderManager.setForcedTechnique("PreShadow"); - - for (int i = 0; i < CAM_NUMBER; i++) { - - //Updating shadow cam with curent split frustra - - ShadowUtil.getOccludersInCamFrustum(occluders, shadowCams[i], splitOccluders); - - //saving light view projection matrix for this split - lightViewProjectionsMatrices[i].set(shadowCams[i].getViewProjectionMatrix()); - renderManager.setCamera(shadowCams[i], false); - - if (debug && frustums[i].getParent() == null) { - ((Node) viewPort.getScenes().get(0)).attachChild(frustums[i]); - } - - r.setFrameBuffer(shadowFB[i]); - r.clearBuffers(false, true, false); - - // System.out.println("Face " + i + " nb occl " + splitOccluders.size()); - // render shadow casters to shadow map - viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCams[i], true); - } - if (flushQueues) { - occluders.clear(); - } - //restore setting for future rendering - r.setFrameBuffer(viewPort.getOutputFrameBuffer()); - renderManager.setForcedMaterial(null); - renderManager.setForcedTechnique(null); - renderManager.setCamera(viewCam, false); - - } - - //debug only : displays depth shadow maps - 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].setWidth(128); - dispPic[i].setHeight(128); - dispPic[i].updateGeometricState(); - renderManager.renderGeometry(dispPic[i]); - } - renderManager.setCamera(cam, false); - } - - /** - * For dubuging purpose Allow to "snapshot" the current frustrum to the - * scene - */ - public void displayDebug() { - debug = true; - } - - public void postFrame(FrameBuffer out) { - - 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); - } - - //forcing the post shadow technique and render state - renderManager.setForcedTechnique(postTechniqueName); - - //rendering the post shadow pass - viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues); - - //resetting renderManager settings - renderManager.setForcedTechnique(null); - renderManager.setForcedMaterial(null); - renderManager.setCamera(cam, false); - - } - - } - - private void updateShadowCams() { - //bottom shadowCams[0].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y.mult(-1f)); @@ -505,7 +101,22 @@ public class PointLightShadowRenderer implements SceneProcessor { shadowCams[i].updateViewProjection(); } - if (debug && frustums == null) { + } + + @Override + protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers) { + ShadowUtil.getOccludersInCamFrustum(sceneOccluders, shadowCams[shadowMapIndex], shadowMapOccluders); + return shadowMapOccluders; + } + + @Override + protected Camera getShadowCam(int shadowMapIndex) { + return shadowCams[shadowMapIndex]; + } + + @Override + protected void doDisplayFrustumDebug(int shadowMapIndex) { + if (frustums == null) { frustums = new Geometry[CAM_NUMBER]; Vector3f[] points = new Vector3f[8]; for (int i = 0; i < 8; i++) { @@ -516,220 +127,14 @@ public class PointLightShadowRenderer implements SceneProcessor { frustums[i] = createFrustum(points, i); } } - - - } - private Geometry[] frustums = null; - - private void setMatParams() { - - GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive); - - //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 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("ShadowMapSize") == null) { - mat.setFloat("ShadowMapSize", shadowMapSize); - } - for (int j = 0; j < CAM_NUMBER; j++) { - mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); - } - mat.setVector3("LightPos", light.getPosition()); - if (mat.getParam("ShadowMap0") == null) { - for (int j = 0; j < CAM_NUMBER; j++) { - mat.setTexture("ShadowMap" + j, shadowMaps[j]); - } - } - if (applyHWShadows || mat.getParam("HardwareShadows") == null) { - mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); - } - if (applyFilterMode || mat.getParam("FilterMode") == null) { - mat.setInt("FilterMode", filterMode.ordinal()); - } - if (mat.getParam("PCFEdge") == null || applyPCFEdge) { - mat.setFloat("PCFEdge", edgesThickness); - } - - if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) { - mat.setFloat("ShadowIntensity", shadowIntensity); - } - - if (fadeInfo != null && mat.getParam("FadeInfo") == null || applyFadeInfo) { - mat.setVector2("FadeInfo", fadeInfo); - } - - } - - applyHWShadows = false; - applyFilterMode = false; - applyPCFEdge = false; - applyShadowIntensity = false; - applyFadeInfo = 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) { - setPostShadowParams(); - } - - } - - protected void setPostShadowParams() { - for (int j = 0; j < CAM_NUMBER; j++) { - postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); - } - postshadowMat.setVector3("LightPos", light.getPosition()); - } - - public void preFrame(float tpf) { - } - - public void cleanup() { - } - - public void reshape(ViewPort vp, int w, int h) { - } - - /** - * returns the shdaow intensity - * - * @see #setShadowIntensity(float shadowIntensity) - * @return shadowIntensity - */ - public float getShadowIntensity() { - return shadowIntensity; - } - - /** - * 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) { - this.shadowIntensity = shadowIntensity; - postshadowMat.setFloat("ShadowIntensity", shadowIntensity); - applyShadowIntensity = true; - } - - /** - * How far the shadows are rendered in the view - * - * @see #setShadowZExtend(float zFar) - * @return shadowZExtend - */ - public float getShadowZExtend() { - return zFarOverride; - } - - /** - * 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) { - if (fadeInfo != null) { - fadeInfo.set(zFar - fadeLength, 1f / fadeLength); - } - this.zFarOverride = zFar; - - } - - /** - * returns the edges thickness - * - * @see #setEdgesThickness(int edgesThickness) - * @return edgesThickness - */ - public int getEdgesThickness() { - return (int) (edgesThickness * 10); - } - - /** - * 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) { - this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10)); - this.edgesThickness *= 0.1f; - postshadowMat.setFloat("PCFEdge", edgesThickness); - applyPCFEdge = true; - } - - /** - * returns true if the PssmRenderer flushed the shadow queues - * - * @return flushQueues - */ - public boolean isFlushQueues() { - return flushQueues; - } - - /** - * 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) { - this.flushQueues = flushQueues; - } - - /** - * Define the length over which the shadow will fade out when using a - * shadowZextend This is useful to make dynamic shadows fade into baked - * shadows in the distance. - * - * @param length the fade length in world units - */ - public void setShadowZFadeLength(float length) { - if (length == 0) { - fadeInfo = null; - fadeLength = 0; - postshadowMat.clearParam("FadeInfo"); - } else { - if (zFarOverride == 0) { - fadeInfo = new Vector2f(0, 0); - } else { - fadeInfo = new Vector2f(zFarOverride - length, 1.0f / length); - } - fadeLength = length; - postshadowMat.setVector2("FadeInfo", fadeInfo); + if (frustums[shadowMapIndex].getParent() == null) { + ((Node) viewPort.getScenes().get(0)).attachChild(frustums[shadowMapIndex]); } } - /** - * get the length over which the shadow will fade out when using a - * shadowZextend - * - * @return the fade length in world units - */ - public float getShadowZFadeLength() { - if (fadeInfo != null) { - return zFarOverride - fadeInfo.x; - } - return 0f; + @Override + protected void setMaterialParameters(Material material) { + material.setVector3("LightPos", light.getPosition()); } /** @@ -748,6 +153,5 @@ public class PointLightShadowRenderer implements SceneProcessor { */ public void setLight(PointLight light) { this.light = light; - updateShadowCams(); } } diff --git a/engine/src/core/com/jme3/shadow/PssmShadowFilter.java b/engine/src/core/com/jme3/shadow/PssmShadowFilter.java index b3bc69134..eda1c5ee6 100644 --- a/engine/src/core/com/jme3/shadow/PssmShadowFilter.java +++ b/engine/src/core/com/jme3/shadow/PssmShadowFilter.java @@ -61,7 +61,9 @@ import java.io.IOException; * API is basically the same as the PssmShadowRenderer; * * @author Rémy Bouquet aka Nehon + * @deprecated use {@link DirectionalLightShadowFilter} */ +@Deprecated public class PssmShadowFilter extends Filter { private PssmShadowRenderer pssmRenderer; diff --git a/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java b/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java index 46544016e..526ee23f9 100644 --- a/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java +++ b/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java @@ -48,6 +48,7 @@ import com.jme3.renderer.queue.OpaqueComparator; import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; +import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.debug.WireFrustum; import com.jme3.texture.FrameBuffer; @@ -66,17 +67,21 @@ import java.util.List; * one.
splits are distributed so that the closer they are from the camera, * the smaller they are to maximize the resolution used of the shadow map.
* This result in a better quality shadow than standard shadow mapping.
for - * more informations on this read this - * http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
+ * more informations on this read this http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
*

* @author Rémy Bouquet aka Nehon + * @deprecated use {@link DirectionalLightShadowRenderer} */ +@Deprecated public class PssmShadowRenderer implements SceneProcessor { /** * FilterMode specifies how shadows are filtered + * @deprecated use {@link EdgeFilteringMode} */ - public enum FilterMode { + @Deprecated + public enum FilterMode{ /** * Shadows are not filtered. Nearest sample is used, causing in blocky @@ -89,30 +94,32 @@ public class PssmShadowRenderer implements SceneProcessor { */ Bilinear, /** - * Dither-based sampling is used, very cheap but can look bad - * at low resolutions. + * Dither-based sampling is used, very cheap but can look bad at low + * resolutions. */ Dither, /** - * 4x4 percentage-closer filtering is used. Shadows will be smoother - * at the cost of performance + * 4x4 percentage-closer filtering is used. Shadows will be smoother at + * the cost of performance */ PCF4, /** - * 8x8 percentage-closer filtering is used. Shadows will be smoother - * at the cost of performance + * 8x8 percentage-closer filtering is used. Shadows will be smoother at + * the cost of performance */ PCFPOISSON, /** - * 8x8 percentage-closer filtering is used. Shadows will be smoother - * at the cost of performance + * 8x8 percentage-closer filtering is used. Shadows will be smoother at + * the cost of performance */ PCF8 } /** - * Specifies the shadow comparison mode + * Specifies the shadow comparison mode + * @deprecated use {@link CompareMode} */ + @Deprecated public enum CompareMode { /** @@ -169,24 +176,30 @@ public class PssmShadowRenderer implements SceneProcessor { protected boolean applyFadeInfo = false; /** - * Create a PSSM Shadow Renderer - * More info on the technique at http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html + * Create a PSSM Shadow Renderer 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). - * @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps). + * @param nbSplits the number of shadow maps rendered (the more shadow maps + * the more quality, the less fps). + * @param nbSplits the number of shadow maps rendered (the more shadow maps + * the more quality, the less fps). */ public PssmShadowRenderer(AssetManager manager, int size, int nbSplits) { - this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md")); + this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadow.j3md")); } /** - * Create a PSSM Shadow Renderer - * More info on the technique at http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html + * Create a PSSM Shadow Renderer 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). - * @param postShadowMat the material used for post shadows if you need to override it + * @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 */ protected PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Material postShadowMat) { @@ -219,7 +232,7 @@ public class PssmShadowRenderer implements SceneProcessor { //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash) shadowFB[i].setColorTexture(dummyTex); - postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]); + postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]); //quads for debuging purpose dispPic[i] = new Picture("Picture" + i); @@ -240,8 +253,10 @@ public class PssmShadowRenderer implements SceneProcessor { } /** - * Sets the filtering mode for shadow edges see {@link FilterMode} for more info - * @param filterMode + * Sets the filtering mode for shadow edges see {@link FilterMode} for more + * info + * + * @param filterMode */ final public void setFilterMode(FilterMode filterMode) { if (filterMode == null) { @@ -271,7 +286,8 @@ public class PssmShadowRenderer implements SceneProcessor { /** * sets the shadow compare mode see {@link CompareMode} for more info - * @param compareMode + * + * @param compareMode */ final public void setCompareMode(CompareMode compareMode) { if (compareMode == null) { @@ -351,7 +367,8 @@ public class PssmShadowRenderer implements SceneProcessor { /** * returns the light direction used by the processor - * @return + * + * @return */ public Vector3f getDirection() { return direction; @@ -359,7 +376,8 @@ public class PssmShadowRenderer implements SceneProcessor { /** * Sets the light direction to use to compute shadows - * @param direction + * + * @param direction */ public void setDirection(Vector3f direction) { this.direction.set(direction).normalizeLocal(); @@ -425,12 +443,23 @@ public class PssmShadowRenderer implements SceneProcessor { lightViewProjectionsMatrices[i].set(shadowCam.getViewProjectionMatrix()); renderManager.setCamera(shadowCam, false); + if (debugfrustums) { +// frustrumFromBound(b.casterBB,ColorRGBA.Blue ); +// frustrumFromBound(b.receiverBB,ColorRGBA.Green ); +// frustrumFromBound(b.splitBB,ColorRGBA.Yellow ); + ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, i)); + ShadowUtil.updateFrustumPoints2(shadowCam, points); + ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, i)); + + } + r.setFrameBuffer(shadowFB[i]); r.clearBuffers(false, true, false); // render shadow casters to shadow map viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCam, true); } + debugfrustums = false; if (flushQueues) { occluders.clear(); } @@ -441,6 +470,11 @@ public class PssmShadowRenderer implements SceneProcessor { renderManager.setCamera(viewCam, false); } + boolean debugfrustums = false; + + public void displayFrustum() { + debugfrustums = true; + } //debug only : displays depth shadow maps protected void displayShadowMap(Renderer r) { @@ -457,8 +491,9 @@ public class PssmShadowRenderer implements SceneProcessor { renderManager.setCamera(cam, false); } - /**For dubuging purpose - * Allow to "snapshot" the current frustrum to the scene + /** + * For dubuging purpose Allow to "snapshot" the current frustrum to the + * scene */ public void displayDebug() { debug = true; @@ -515,36 +550,22 @@ public class PssmShadowRenderer implements SceneProcessor { //iterating through the mat cache and setting the parameters for (Material mat : matCache) { - if (mat.getParam("Splits") == null) { - mat.setColor("Splits", splits); - } - if (mat.getParam("ShadowMapSize") == null) { - mat.setFloat("ShadowMapSize", shadowMapSize); - } + mat.setColor("Splits", splits); + 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.setTexture("ShadowMap" + j, shadowMaps[j]); - } - } - if (applyHWShadows || mat.getParam("HardwareShadows") == null) { - mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); - } - if (applyFilterMode || mat.getParam("FilterMode") == null) { - mat.setInt("FilterMode", filterMode.ordinal()); - } - if (mat.getParam("PCFEdge") == null || applyPCFEdge) { - mat.setFloat("PCFEdge", edgesThickness); + for (int j = 0; j < nbSplits; j++) { + mat.setTexture("ShadowMap" + j, shadowMaps[j]); } + mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); + mat.setInt("FilterMode", filterMode.ordinal()); + mat.setFloat("PCFEdge", edgesThickness); + mat.setFloat("ShadowIntensity", shadowIntensity); - if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) { - mat.setFloat("ShadowIntensity", shadowIntensity); - } - - if (fadeInfo != null && mat.getParam("FadeInfo") == null || applyFadeInfo) { - mat.setVector2("FadeInfo", fadeInfo); + if (fadeInfo != null) { + mat.setVector2("FadeInfo", fadeInfo); } } @@ -567,7 +588,8 @@ public class PssmShadowRenderer implements SceneProcessor { postshadowMat.setColor("Splits", splits); for (int j = 0; j < nbSplits; j++) { postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); - } + postshadowMat.setTexture("ShadowMap" + j, shadowMaps[j]); + } } public void preFrame(float tpf) { @@ -580,8 +602,8 @@ public class PssmShadowRenderer implements SceneProcessor { } /** - * returns the labda parameter - * see #setLambda(float lambda) + * returns the labda parameter see #setLambda(float lambda) + * * @return lambda */ public float getLambda() { @@ -602,6 +624,7 @@ public class PssmShadowRenderer implements SceneProcessor { /** * How far the shadows are rendered in the view + * * @see #setShadowZExtend(float zFar) * @return shadowZExtend */ @@ -610,20 +633,23 @@ public class PssmShadowRenderer implements SceneProcessor { } /** - * 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. + * 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) { - if(fadeInfo!=null){ + if (fadeInfo != null) { fadeInfo.set(zFar - fadeLength, 1f / fadeLength); } this.zFarOverride = zFar; - + } /** * returns the shdaow intensity + * * @see #setShadowIntensity(float shadowIntensity) * @return shadowIntensity */ @@ -632,10 +658,10 @@ public class PssmShadowRenderer implements SceneProcessor { } /** - * 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 + * 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) { @@ -646,6 +672,7 @@ public class PssmShadowRenderer implements SceneProcessor { /** * returns the edges thickness + * * @see #setEdgesThickness(int edgesThickness) * @return edgesThickness */ @@ -654,8 +681,10 @@ public class PssmShadowRenderer implements SceneProcessor { } /** - * 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 + * 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) { this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10)); @@ -666,6 +695,7 @@ public class PssmShadowRenderer implements SceneProcessor { /** * returns true if the PssmRenderer flushed the shadow queues + * * @return flushQueues */ public boolean isFlushQueues() { @@ -673,18 +703,21 @@ public class PssmShadowRenderer implements SceneProcessor { } /** - * 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 + * 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) { this.flushQueues = flushQueues; } - + /** * Define the length over which the shadow will fade out when using a - * shadowZextend - * This is useful to make dynamic shadows fade into baked shadows in the distance. + * shadowZextend This is useful to make dynamic shadows fade into baked + * shadows in the distance. + * * @param length the fade length in world units */ public void setShadowZFadeLength(float length) { @@ -702,15 +735,17 @@ public class PssmShadowRenderer implements SceneProcessor { postshadowMat.setVector2("FadeInfo", fadeInfo); } } - - /** - * get the length over which the shadow will fade out when using a shadowZextend + + /** + * get the length over which the shadow will fade out when using a + * shadowZextend + * * @return the fade length in world units */ - public float getShadowZFadeLength(){ - if(fadeInfo!=null){ + public float getShadowZFadeLength() { + if (fadeInfo != null) { return zFarOverride - fadeInfo.x; } - return 0f; + return 0f; } } diff --git a/engine/src/core/com/jme3/shadow/ShadowUtil.java b/engine/src/core/com/jme3/shadow/ShadowUtil.java index 480b2ba05..4411c3418 100644 --- a/engine/src/core/com/jme3/shadow/ShadowUtil.java +++ b/engine/src/core/com/jme3/shadow/ShadowUtil.java @@ -361,9 +361,7 @@ public class ShadowUtil { if (ortho) { shadowCam.setFrustum(-1, 1, -1, 1, 1, -1); - } else { - shadowCam.setFrustumPerspective(45, 1, 1, 150); - } + } // create transform to rotate points to viewspace Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix(); @@ -442,9 +440,9 @@ public class ShadowUtil { splitMin.z = 0; - if (!ortho) { - shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z); - } +// if (!ortho) { +// shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z); +// } Matrix4f projMatrix = shadowCam.getProjectionMatrix(); diff --git a/engine/src/core/com/jme3/shadow/SpotLightShadowFilter.java b/engine/src/core/com/jme3/shadow/SpotLightShadowFilter.java new file mode 100644 index 000000000..5aa48015a --- /dev/null +++ b/engine/src/core/com/jme3/shadow/SpotLightShadowFilter.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2009-2012 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.light.SpotLight; +import java.io.IOException; + +/** + * + * This Filter does basically the same as a SpotLightShadowRenderer + * 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 SpotLightShadowFilter extends AbstractShadowFilter { + + + /** + * Creates a SpotLight Shadow Filter + * @param assetManager the application asset manager + * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, + * etc...) + * the more quality, the less fps). + */ + public SpotLightShadowFilter(AssetManager assetManager, int shadowMapSize) { + super(assetManager, shadowMapSize, new SpotLightShadowRenderer(assetManager, shadowMapSize)); + } + + /** + * return the light used to cast shadows + * + * @return the SpotLight + */ + public SpotLight getLight() { + return shadowRenderer.getLight(); + } + + /** + * Sets the light to use to cast shadows + * + * @param light a SpotLight + */ + public void setLight(SpotLight light) { + shadowRenderer.setLight(light); + } + + /** + * How far the shadows are rendered in the view + * + * @see setShadowZExtend(float zFar) + * @return shadowZExtend + */ + public float getShadowZExtend() { + return shadowRenderer.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) { + shadowRenderer.setShadowZExtend(zFar); + } + + /** + * Define the length over which the shadow will fade out when using a + * shadowZextend + * + * @param length the fade length in world units + */ + public void setShadowZFadeLength(float length) { + shadowRenderer.setShadowZFadeLength(length); + } + + /** + * get the length over which the shadow will fade out when using a + * shadowZextend + * + * @return the fade length in world units + */ + public float getShadowZFadeLength() { + return shadowRenderer.getShadowZFadeLength(); + } + + @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/SpotLightShadowRenderer.java b/engine/src/core/com/jme3/shadow/SpotLightShadowRenderer.java new file mode 100644 index 000000000..d318b6518 --- /dev/null +++ b/engine/src/core/com/jme3/shadow/SpotLightShadowRenderer.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2009-2012 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.light.DirectionalLight; +import com.jme3.light.SpotLight; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.math.Vector4f; +import com.jme3.renderer.Camera; +import com.jme3.renderer.queue.GeometryList; +import com.jme3.renderer.queue.OpaqueComparator; +import com.jme3.scene.Node; + +/** + * SpotLightShadowRenderer renderer use Parrallel Split Shadow Mapping technique + * (pssm)
It splits the view frustum in several parts and compute a shadow + * map for each one.
splits are distributed so that the closer they are from + * the camera, the smaller they are to maximize the resolution used of the + * shadow map.
This result in a better quality shadow than standard shadow + * mapping.
for more informations on this read this http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
+ *

+ * @author Rémy Bouquet aka Nehon + */ +public class SpotLightShadowRenderer extends AbstractShadowRenderer { + + protected float zFarOverride = 0; + protected Camera shadowCam; + protected GeometryList mapOccluders = new GeometryList(new OpaqueComparator()); + protected SpotLight light; + protected Vector3f[] points = new Vector3f[8]; + //Holding the info for fading shadows in the far distance + protected Vector2f fadeInfo; + protected float fadeLength; + + /** + * Create a SpotLightShadowRenderer This use standard shadow mapping + * + * @param assetManager the application asset manager + * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, + * etc...) the more quality, the less fps). + */ + public SpotLightShadowRenderer(AssetManager assetManager, int shadowMapSize) { + super(assetManager, shadowMapSize, 1); + + shadowCam = new Camera(shadowMapSize, shadowMapSize); + + for (int i = 0; i < points.length; i++) { + points[i] = new Vector3f(); + } + } + + /** + * return the light used to cast shadows + * + * @return the SpotLight + */ + public SpotLight getLight() { + return light; + } + + /** + * Sets the light to use to cast shadows + * + * @param light a SpotLight + */ + public void setLight(SpotLight light) { + this.light = light; + } + + @Override + protected void updateShadowCams(Camera viewCam) { + + float zFar = zFarOverride; + if (zFar == 0) { + zFar = viewCam.getFrustumFar(); + } + + //We prevent computing the frustum points and splits with zeroed or negative near clip value + float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f); + ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points); + //shadowCam.setDirection(direction); + + shadowCam.setFrustumPerspective(light.getSpotOuterAngle() * FastMath.RAD_TO_DEG * 2.0f, 1, 1f, light.getSpotRange()); + shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp()); + shadowCam.setLocation(light.getPosition()); + + shadowCam.update(); + shadowCam.updateViewProjection(); + + } + + @Override + protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers) { + ShadowUtil.getOccludersInCamFrustum(sceneOccluders, shadowCam, mapOccluders); + return mapOccluders; + } + + @Override + protected Camera getShadowCam(int shadowMapIndex) { + return shadowCam; + } + + @Override + protected void doDisplayFrustumDebug(int shadowMapIndex) { + Vector3f[] points2 = points.clone(); + + ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, shadowMapIndex)); + ShadowUtil.updateFrustumPoints2(shadowCam, points2); + ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points2, shadowMapIndex)); + } + + @Override + protected void setMaterialParameters(Material material) { + } + + /** + * How far the shadows are rendered in the view + * + * @see #setShadowZExtend(float zFar) + * @return shadowZExtend + */ + public float getShadowZExtend() { + return zFarOverride; + } + + /** + * 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) { + if (fadeInfo != null) { + fadeInfo.set(zFar - fadeLength, 1f / fadeLength); + } + this.zFarOverride = zFar; + + } + + /** + * Define the length over which the shadow will fade out when using a + * shadowZextend This is useful to make dynamic shadows fade into baked + * shadows in the distance. + * + * @param length the fade length in world units + */ + public void setShadowZFadeLength(float length) { + if (length == 0) { + fadeInfo = null; + fadeLength = 0; + postshadowMat.clearParam("FadeInfo"); + } else { + if (zFarOverride == 0) { + fadeInfo = new Vector2f(0, 0); + } else { + fadeInfo = new Vector2f(zFarOverride - length, 1.0f / length); + } + fadeLength = length; + postshadowMat.setVector2("FadeInfo", fadeInfo); + } + } + + /** + * get the length over which the shadow will fade out when using a + * shadowZextend + * + * @return the fade length in world units + */ + public float getShadowZFadeLength() { + if (fadeInfo != null) { + return zFarOverride - fadeInfo.x; + } + return 0f; + } +} diff --git a/engine/src/test/jme3test/light/ShadowTestUIManager.java b/engine/src/test/jme3test/light/ShadowTestUIManager.java new file mode 100644 index 000000000..34bc20d89 --- /dev/null +++ b/engine/src/test/jme3test/light/ShadowTestUIManager.java @@ -0,0 +1,154 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package jme3test.light; + +import com.jme3.asset.AssetManager; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.input.InputManager; +import com.jme3.input.KeyInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.renderer.Camera; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Node; +import com.jme3.shadow.AbstractShadowFilter; +import com.jme3.shadow.AbstractShadowRenderer; +import com.jme3.shadow.CompareMode; +import com.jme3.shadow.EdgeFilteringMode; + +/** + * + * @author Nehon + */ +public class ShadowTestUIManager implements ActionListener { + + private BitmapText shadowTypeText; + private BitmapText shadowCompareText; + private BitmapText shadowFilterText; + private BitmapText shadowIntensityText; + private final static String TYPE_TEXT = "(Space) Shadow type : "; + private final static String COMPARE_TEXT = "(enter) Shadow compare "; + private final static String FILTERING_TEXT = "(f) Edge filtering : "; + private final static String INTENSITY_TEXT = "(t:up, g:down) Shadow intensity : "; + private boolean hardwareShadows = true; + private AbstractShadowRenderer plsr; + private AbstractShadowFilter plsf; + private ViewPort viewPort; + private int filteringIndex = 0; + private int renderModeIndex = 0; + + + public ShadowTestUIManager(AssetManager assetManager,AbstractShadowRenderer plsr, AbstractShadowFilter plsf, + Node guiNode, InputManager inputManager, ViewPort viewPort) { + this.plsr = plsr; + this.plsf = plsf; + this.viewPort = viewPort; + BitmapFont guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); + shadowTypeText = createText(guiFont); + shadowCompareText = createText(guiFont); + shadowFilterText = createText(guiFont); + shadowIntensityText = createText(guiFont); + + shadowTypeText.setText(TYPE_TEXT + "Processor"); + shadowCompareText.setText(COMPARE_TEXT + (hardwareShadows ? "Hardware" : "Software")); + shadowFilterText.setText(FILTERING_TEXT + plsr.getEdgeFilteringMode().toString()); + shadowIntensityText.setText(INTENSITY_TEXT + plsr.getShadowIntensity()); + + shadowTypeText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 20, 0); + shadowCompareText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 40, 0); + shadowFilterText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 60, 0); + shadowIntensityText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 80, 0); + + guiNode.attachChild(shadowTypeText); + guiNode.attachChild(shadowCompareText); + guiNode.attachChild(shadowFilterText); + guiNode.attachChild(shadowIntensityText); + + inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); + inputManager.addMapping("changeFiltering", new KeyTrigger(KeyInput.KEY_F)); + inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T)); + inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G)); + inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y)); + inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H)); + inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN)); + + + inputManager.addListener(this, "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering"); + + } + + + public void onAction(String name, boolean keyPressed, float tpf) { + if (name.equals("toggle") && keyPressed) { + renderModeIndex += 1; + renderModeIndex %= 3; + + switch (renderModeIndex) { + case 0: + viewPort.addProcessor(plsr); + shadowTypeText.setText(TYPE_TEXT + "Processor"); + break; + case 1: + viewPort.removeProcessor(plsr); + plsf.setEnabled(true); + shadowTypeText.setText(TYPE_TEXT + "Filter"); + break; + case 2: + plsf.setEnabled(false); + shadowTypeText.setText(TYPE_TEXT + "None"); + break; + } + + + + } else if (name.equals("toggleHW") && keyPressed) { + hardwareShadows = !hardwareShadows; + plsr.setShadowCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software); + plsf.setShadowCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software); + + shadowCompareText.setText(COMPARE_TEXT + (hardwareShadows ? "Hardware" : "Software")); + } + + + if (name.equals("changeFiltering") && keyPressed) { + filteringIndex = plsr.getEdgeFilteringMode().ordinal(); + filteringIndex = (filteringIndex + 1) % EdgeFilteringMode.values().length; + EdgeFilteringMode m = EdgeFilteringMode.values()[filteringIndex]; + plsr.setEdgeFilteringMode(m); + plsf.setEdgeFilteringMode(m); + shadowFilterText.setText(FILTERING_TEXT + m.toString()); + } + + if (name.equals("ShadowUp") && keyPressed) { + plsr.setShadowIntensity(plsr.getShadowIntensity() + 0.1f); + plsf.setShadowIntensity(plsf.getShadowIntensity() + 0.1f); + + shadowIntensityText.setText(INTENSITY_TEXT + plsr.getShadowIntensity()); + } + if (name.equals("ShadowDown") && keyPressed) { + plsr.setShadowIntensity(plsr.getShadowIntensity() - 0.1f); + plsf.setShadowIntensity(plsf.getShadowIntensity() - 0.1f); + shadowIntensityText.setText(INTENSITY_TEXT + plsr.getShadowIntensity()); + } + if (name.equals("ThicknessUp") && keyPressed) { + plsr.setEdgesThickness(plsr.getEdgesThickness() + 1); + plsf.setEdgesThickness(plsf.getEdgesThickness() + 1); + System.out.println("Shadow thickness : " + plsr.getEdgesThickness()); + } + if (name.equals("ThicknessDown") && keyPressed) { + plsr.setEdgesThickness(plsr.getEdgesThickness() - 1); + plsf.setEdgesThickness(plsf.getEdgesThickness() - 1); + System.out.println("Shadow thickness : " + plsr.getEdgesThickness()); + } + + } + + private BitmapText createText(BitmapFont guiFont) { + BitmapText t = new BitmapText(guiFont, false); + t.setSize(guiFont.getCharSet().getRenderedSize() * 0.75f); + return t; + } +} diff --git a/engine/src/test/jme3test/light/TestDirectionalLightShadow.java b/engine/src/test/jme3test/light/TestDirectionalLightShadow.java new file mode 100644 index 000000000..1499b3377 --- /dev/null +++ b/engine/src/test/jme3test/light/TestDirectionalLightShadow.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2009-2012 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.font.BitmapText; +import com.jme3.input.KeyInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.light.AmbientLight; +import com.jme3.light.DirectionalLight; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.post.FilterPostProcessor; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.Geometry; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.AbstractControl; +import com.jme3.scene.control.Control; +import com.jme3.scene.shape.Box; +import com.jme3.scene.shape.Sphere; +import com.jme3.shadow.CompareMode; +import com.jme3.shadow.DirectionalLightShadowFilter; +import com.jme3.shadow.DirectionalLightShadowRenderer; +import com.jme3.shadow.EdgeFilteringMode; +import com.jme3.shadow.PssmShadowRenderer.FilterMode; +import com.jme3.texture.Texture; +import com.jme3.texture.Texture.WrapMode; +import com.jme3.util.SkyFactory; +import com.jme3.util.TangentBinormalGenerator; + +public class TestDirectionalLightShadow extends SimpleApplication implements ActionListener { + + private Spatial[] obj; + private Material[] mat; + private boolean hardwareShadows = false; + private DirectionalLightShadowRenderer dlsr; + private DirectionalLightShadowFilter dlsf; + private Geometry ground; + private Material matGroundU; + private Material matGroundL; + + public static void main(String[] args) { + TestDirectionalLightShadow app = new TestDirectionalLightShadow(); + app.start(); + } + + public void loadScene() { + obj = new Spatial[2]; + mat = new Material[2]; + mat[0] = assetManager.loadMaterial("Common/Materials/RedColor.j3m"); + mat[1] = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); + mat[1].setBoolean("UseMaterialColors", true); + mat[1].setColor("Ambient", ColorRGBA.White.mult(0.5f)); + mat[1].setColor("Diffuse", ColorRGBA.White.clone()); + + + obj[0] = new Geometry("sphere", new Sphere(30, 30, 2)); + obj[0].setShadowMode(ShadowMode.CastAndReceive); + obj[1] = new Geometry("cube", new Box(1.0f, 1.0f, 1.0f)); + obj[1].setShadowMode(ShadowMode.CastAndReceive); + TangentBinormalGenerator.generate(obj[1]); + TangentBinormalGenerator.generate(obj[0]); + + + for (int i = 0; i < 60; i++) { + Spatial t = obj[FastMath.nextRandomInt(0, obj.length - 1)].clone(false); + t.setLocalScale(FastMath.nextRandomFloat() * 10f); + t.setMaterial(mat[FastMath.nextRandomInt(0, mat.length - 1)]); + rootNode.attachChild(t); + t.setLocalTranslation(FastMath.nextRandomFloat() * 200f, FastMath.nextRandomFloat() * 30f + 20, 30f * (i + 2f)); + } + + Box b = new Box(new Vector3f(0, 10, 550), 1000, 2, 1000); + b.scaleTextureCoordinates(new Vector2f(10, 10)); + ground = new Geometry("soil", b); + matGroundU = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + matGroundU.setColor("Color", ColorRGBA.Green); + + + matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + grass.setWrap(WrapMode.Repeat); + matGroundL.setTexture("DiffuseMap", grass); + + ground.setMaterial(matGroundL); + + ground.setShadowMode(ShadowMode.CastAndReceive); + rootNode.attachChild(ground); + + l = new DirectionalLight(); + //new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal() + l.setDirection(new Vector3f(-1, -1, -1)); + rootNode.addLight(l); + + + AmbientLight al = new AmbientLight(); + al.setColor(ColorRGBA.White.mult(0.5f)); + rootNode.addLight(al); + + Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false); + sky.setLocalScale(350); + + rootNode.attachChild(sky); + } + DirectionalLight l; + + @Override + public void simpleInitApp() { + // put the camera in a bad position + cam.setLocation(new Vector3f(65.25412f, 44.38738f, 9.087874f)); + cam.setRotation(new Quaternion(0.078139365f, 0.050241485f, -0.003942559f, 0.9956679f)); + + flyCam.setMoveSpeed(100); + + loadScene(); + + dlsr = new DirectionalLightShadowRenderer(assetManager, 1024, 3); + dlsr.setLight(l); + dlsr.setLambda(0.55f); + dlsr.setShadowIntensity(0.6f); + dlsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest); + //dlsr.displayFrustum(); + viewPort.addProcessor(dlsr); + + dlsf = new DirectionalLightShadowFilter(assetManager, 1024, 3); + dlsf.setLight(l); + dlsf.setLambda(0.55f); + dlsf.setShadowIntensity(0.6f); + dlsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest); + dlsf.setEnabled(false); + + FilterPostProcessor fpp = new FilterPostProcessor(assetManager); + fpp.addFilter(dlsf); + + viewPort.addProcessor(fpp); + + initInputs(); + } + + private void initInputs() { + + inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y)); + inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H)); + inputManager.addMapping("lambdaUp", new KeyTrigger(KeyInput.KEY_U)); + inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J)); + inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M)); + inputManager.addMapping("splits", new KeyTrigger(KeyInput.KEY_X)); + + inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_NUMPAD8)); + inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_NUMPAD2)); + inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_NUMPAD6)); + inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_NUMPAD4)); + inputManager.addMapping("fwd", new KeyTrigger(KeyInput.KEY_PGUP)); + inputManager.addMapping("back", new KeyTrigger(KeyInput.KEY_PGDN)); + + + inputManager.addListener(this, "lambdaUp", "lambdaDown", "ThicknessUp", "ThicknessDown", + "switchGroundMat", "splits", "up", "down", "right", "left", "fwd", "back"); + + ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, dlsr, dlsf, guiNode, inputManager, viewPort); + } + + + + + public void onAction(String name, boolean keyPressed, float tpf) { + + + + if (name.equals("lambdaUp") && keyPressed) { + dlsr.setLambda(dlsr.getLambda() + 0.01f); + dlsf.setLambda(dlsr.getLambda() + 0.01f); + System.out.println("Lambda : " + dlsr.getLambda()); + } else if (name.equals("lambdaDown") && keyPressed) { + dlsr.setLambda(dlsr.getLambda() - 0.01f); + dlsf.setLambda(dlsr.getLambda() - 0.01f); + System.out.println("Lambda : " + dlsr.getLambda()); + } + + if (name.equals("ShadowUp") && keyPressed) { + dlsr.setShadowIntensity(dlsr.getShadowIntensity() + 0.1f); + dlsf.setShadowIntensity(dlsr.getShadowIntensity() + 0.1f); + System.out.println("Shadow intensity : " + dlsr.getShadowIntensity()); + } + if (name.equals("ShadowDown") && keyPressed) { + dlsr.setShadowIntensity(dlsr.getShadowIntensity() - 0.1f); + dlsf.setShadowIntensity(dlsr.getShadowIntensity() - 0.1f); + System.out.println("Shadow intensity : " + dlsr.getShadowIntensity()); + } + if (name.equals("ThicknessUp") && keyPressed) { + dlsr.setEdgesThickness(dlsr.getEdgesThickness() + 1); + dlsf.setEdgesThickness(dlsr.getEdgesThickness() + 1); + System.out.println("Shadow thickness : " + dlsr.getEdgesThickness()); + } + if (name.equals("ThicknessDown") && keyPressed) { + dlsr.setEdgesThickness(dlsr.getEdgesThickness() - 1); + dlsf.setEdgesThickness(dlsr.getEdgesThickness() - 1); + System.out.println("Shadow thickness : " + dlsr.getEdgesThickness()); + } + if (name.equals("switchGroundMat") && keyPressed) { + if (ground.getMaterial() == matGroundL) { + ground.setMaterial(matGroundU); + } else { + ground.setMaterial(matGroundL); + } + } + + if (name.equals("up")) { + up = keyPressed; + } + if (name.equals("down")) { + down = keyPressed; + } + if (name.equals("right")) { + right = keyPressed; + } + if (name.equals("left")) { + left = keyPressed; + } + if (name.equals("fwd")) { + fwd = keyPressed; + } + if (name.equals("back")) { + back = keyPressed; + } + + } + boolean up = false; + boolean down = false; + boolean left = false; + boolean right = false; + boolean fwd = false; + boolean back = false; + float time = 0; + float s = 1f; + + @Override + public void simpleUpdate(float tpf) { + if (up) { + Vector3f v = l.getDirection(); + v.y += tpf / s; + setDir(v); + } + if (down) { + Vector3f v = l.getDirection(); + v.y -= tpf / s; + setDir(v); + } + if (right) { + Vector3f v = l.getDirection(); + v.x += tpf / s; + setDir(v); + } + if (left) { + Vector3f v = l.getDirection(); + v.x -= tpf / s; + setDir(v); + } + if (fwd) { + Vector3f v = l.getDirection(); + v.z += tpf / s; + setDir(v); + } + if (back) { + Vector3f v = l.getDirection(); + v.z -= tpf / s; + setDir(v); + } + + } + + private void setDir(Vector3f v) { + l.setDirection(v); + } +} diff --git a/engine/src/test/jme3test/light/TestPointLightShadows.java b/engine/src/test/jme3test/light/TestPointLightShadows.java index 57aec17e6..40dbb2838 100644 --- a/engine/src/test/jme3test/light/TestPointLightShadows.java +++ b/engine/src/test/jme3test/light/TestPointLightShadows.java @@ -32,12 +32,7 @@ package jme3test.light; import com.jme3.app.SimpleApplication; -import com.jme3.font.BitmapText; -import com.jme3.input.KeyInput; -import com.jme3.input.controls.ActionListener; -import com.jme3.input.controls.KeyTrigger; import com.jme3.light.PointLight; -import com.jme3.math.FastMath; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; import com.jme3.post.FilterPostProcessor; @@ -46,18 +41,18 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; +import com.jme3.shadow.EdgeFilteringMode; import com.jme3.shadow.PointLightShadowFilter; import com.jme3.shadow.PointLightShadowRenderer; -public class TestPointLightShadows extends SimpleApplication implements ActionListener { +public class TestPointLightShadows extends SimpleApplication { public static void main(String[] args) { TestPointLightShadows app = new TestPointLightShadows(); app.start(); } Node lightNode; - private boolean hardwareShadows = true; - PointLightShadowRenderer plsr ; + PointLightShadowRenderer plsr; PointLightShadowFilter plsf; @Override @@ -91,147 +86,44 @@ public class TestPointLightShadows extends SimpleApplication implements ActionLi plsr = new PointLightShadowRenderer(assetManager, 512); plsr.setLight((PointLight) scene.getLocalLightList().get(0)); - plsr.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest); - // plsr.displayDebug(); + plsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest); + // plsr.setFlushQueues(false); + // plsr.displayDebug(); viewPort.addProcessor(plsr); +// PointLight pl = new PointLight(); +// pl.setPosition(new Vector3f(0, 0.5f, 0)); +// pl.setRadius(5); +// rootNode.addLight(pl); + +// Geometry lightMdl2 = new Geometry("Light2", new Sphere(10, 10, 0.1f)); +// //Geometry lightMdl = new Geometry("Light", new Box(.1f,.1f,.1f)); +// lightMdl2.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m")); +// lightMdl2.setShadowMode(RenderQueue.ShadowMode.Off); +// rootNode.attachChild(lightMdl2); +// lightMdl2.setLocalTranslation(pl.getPosition()); +// PointLightShadowRenderer plsr2 = new PointLightShadowRenderer(assetManager, 512); +// plsr2.setLight(pl); +// plsr2.setEdgeFilteringMode(EdgeFilteringMode.Nearest); +// // plsr.displayDebug(); +// viewPort.addProcessor(plsr2); + + plsf = new PointLightShadowFilter(assetManager, 512); plsf.setLight((PointLight) scene.getLocalLightList().get(0)); - plsf.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest); + plsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest); plsf.setEnabled(false); - + FilterPostProcessor fpp = new FilterPostProcessor(assetManager); - fpp.addFilter(plsf); + fpp.addFilter(plsf); viewPort.addProcessor(fpp); - initUIAndInputs(); + ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, plsr, plsf, guiNode, inputManager, viewPort); } - - BitmapText shadowTypeText; - BitmapText shadowCompareText; - BitmapText shadowFilterText; - BitmapText shadowIntensityText; - final static String TYPE_TEXT = "(Space) Shadow type : "; - final static String COMPARE_TEXT = "(enter) Shadow compare "; - final static String FILTERING_TEXT = "(f) Edge filtering : "; - final static String INTENSITY_TEXT = "(t:up, g:down) Shadow intensity : "; - - private void initUIAndInputs() { - guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); - shadowTypeText = createText(); - shadowCompareText = createText(); - shadowFilterText = createText(); - shadowIntensityText = createText(); - - shadowTypeText.setText(TYPE_TEXT+"Processor"); - shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software")); - shadowFilterText.setText(FILTERING_TEXT+plsr.getFilterMode().toString()); - shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity()); - - shadowTypeText.setLocalTranslation(10, cam.getHeight()-20, 0); - shadowCompareText.setLocalTranslation(10, cam.getHeight()-40, 0); - shadowFilterText.setLocalTranslation(10, cam.getHeight()-60, 0); - shadowIntensityText.setLocalTranslation(10, cam.getHeight()-80, 0); - - guiNode.attachChild(shadowTypeText); - guiNode.attachChild(shadowCompareText); - guiNode.attachChild(shadowFilterText); - guiNode.attachChild(shadowIntensityText); - - inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); - inputManager.addMapping("changeFiltering", new KeyTrigger(KeyInput.KEY_F)); - inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T)); - inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G)); - inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y)); - inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H)); - inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN)); - - - inputManager.addListener(this, "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering"); - - } - - int filteringIndex = 0; - int renderModeIndex = 0; - - public void onAction(String name, boolean keyPressed, float tpf) { - if (name.equals("toggle") && keyPressed) { - renderModeIndex += 1; - renderModeIndex %= 3; - - switch (renderModeIndex) { - case 0: - viewPort.addProcessor(plsr); - shadowTypeText.setText(TYPE_TEXT+"Processor"); - break; - case 1: - viewPort.removeProcessor(plsr); - plsf.setEnabled(true); - shadowTypeText.setText(TYPE_TEXT+"Filter"); - break; - case 2: - plsf.setEnabled(false); - shadowTypeText.setText(TYPE_TEXT+"None"); - break; - } - - - - } else if (name.equals("toggleHW") && keyPressed) { - hardwareShadows = !hardwareShadows; - plsr.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software); - plsf.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software); - - shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software")); - } - - - if (name.equals("changeFiltering") && keyPressed) { - filteringIndex = plsr.getFilterMode().ordinal(); - filteringIndex = (filteringIndex + 1) % PointLightShadowRenderer.FilterMode.values().length; - PointLightShadowRenderer.FilterMode m = PointLightShadowRenderer.FilterMode.values()[filteringIndex]; - plsr.setFilterMode(m); - plsf.setFilterMode(m); - shadowFilterText.setText(FILTERING_TEXT+m.toString()); - } - - - if (name.equals("ShadowUp") && keyPressed) { - plsr.setShadowIntensity(plsr.getShadowIntensity() + 0.1f); - plsf.setShadowIntensity(plsr.getShadowIntensity() + 0.1f); - - shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity()); - } - if (name.equals("ShadowDown") && keyPressed) { - plsr.setShadowIntensity(plsr.getShadowIntensity() - 0.1f); - plsf.setShadowIntensity(plsr.getShadowIntensity() - 0.1f); - shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity()); - } - if (name.equals("ThicknessUp") && keyPressed) { - plsr.setEdgesThickness(plsr.getEdgesThickness() + 1); - plsf.setEdgesThickness(plsr.getEdgesThickness() + 1); - System.out.println("Shadow thickness : " + plsr.getEdgesThickness()); - } - if (name.equals("ThicknessDown") && keyPressed) { - plsr.setEdgesThickness(plsr.getEdgesThickness() - 1); - plsf.setEdgesThickness(plsr.getEdgesThickness() - 1); - System.out.println("Shadow thickness : " + plsr.getEdgesThickness()); - } - - } - float time; @Override public void simpleUpdate(float tpf) { - time += tpf; - lightNode.setLocalTranslation(FastMath.cos(time)*0.4f,lightNode.getLocalTranslation().y , FastMath.sin(time)*0.4f); - - } - - private BitmapText createText() { - BitmapText t = new BitmapText(guiFont, false); - t.setSize(guiFont.getCharSet().getRenderedSize()*0.75f); - return t; +// lightNode.move(FastMath.cos(tpf) * 0.4f, 0, FastMath.sin(tpf) * 0.4f); } } \ No newline at end of file diff --git a/engine/src/test/jme3test/light/TestPssmShadow.java b/engine/src/test/jme3test/light/TestPssmShadow.java index 31d890906..4c74b0664 100644 --- a/engine/src/test/jme3test/light/TestPssmShadow.java +++ b/engine/src/test/jme3test/light/TestPssmShadow.java @@ -32,9 +32,13 @@ package jme3test.light; import com.jme3.app.SimpleApplication; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.Savable; import com.jme3.font.BitmapText; import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.AnalogListener; import com.jme3.input.controls.KeyTrigger; import com.jme3.light.AmbientLight; import com.jme3.light.DirectionalLight; @@ -63,6 +67,7 @@ import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapMode; import com.jme3.util.SkyFactory; import com.jme3.util.TangentBinormalGenerator; +import java.io.IOException; public class TestPssmShadow extends SimpleApplication implements ActionListener { @@ -123,7 +128,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener ground.setShadowMode(ShadowMode.CastAndReceive); rootNode.attachChild(ground); - DirectionalLight l = new DirectionalLight(); + l = new DirectionalLight(); l.setDirection(new Vector3f(-1, -1, -1)); rootNode.addLight(l); @@ -136,6 +141,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener rootNode.attachChild(sky); } + DirectionalLight l; @Override public void simpleInitApp() { @@ -148,36 +154,42 @@ 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.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); + pssmRenderer.setDirection(new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal()); pssmRenderer.setLambda(0.55f); pssmRenderer.setShadowIntensity(0.6f); pssmRenderer.setCompareMode(CompareMode.Software); pssmRenderer.setFilterMode(FilterMode.Dither); - // pssmRenderer.displayDebug(); + + pssmRenderer.displayFrustum(); 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.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); + pssmRenderer.setDirection(new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal()); pssmFilter.setLambda(0.55f); pssmFilter.setShadowIntensity(0.6f); pssmFilter.setCompareMode(CompareMode.Software); pssmFilter.setFilterMode(FilterMode.Dither); pssmFilter.setEnabled(false); + + +// pssmFilter.setShadowZFadeLength(300); +// pssmFilter.setShadowZExtend(500); + 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) */ guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); @@ -195,7 +207,20 @@ 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.addMapping("splits", new KeyTrigger(KeyInput.KEY_X)); + + inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_NUMPAD8)); + inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_NUMPAD2)); + inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_NUMPAD6)); + inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_NUMPAD4)); + inputManager.addMapping("fwd", new KeyTrigger(KeyInput.KEY_PGUP)); + inputManager.addMapping("back", new KeyTrigger(KeyInput.KEY_PGDN)); + + + + inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering", + "switchGroundMat", "splits", "up", "down", "right", "left", "fwd", "back"); + } private void print(String str) { @@ -246,10 +271,10 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener break; case 1: viewPort.removeProcessor(pssmRenderer); - pssmFilter.setEnabled(true); + pssmFilter.setEnabled(true); break; case 2: - pssmFilter.setEnabled(false); + pssmFilter.setEnabled(false); break; } @@ -311,5 +336,78 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener } } +// if (name.equals("splits") && keyPressed) { +// pssmRenderer.displayFrustum(); +// } + + + if (name.equals("up")) { + up = keyPressed; + } + if (name.equals("down")) { + down = keyPressed; + } + if (name.equals("right")) { + right = keyPressed; + } + if (name.equals("left") ) { + left = keyPressed; + } + if (name.equals("fwd")) { + fwd = keyPressed; + } + if (name.equals("back")) { + back = keyPressed; + } + + } + boolean up = false; + boolean down = false; + boolean left = false; + boolean right = false; + boolean fwd = false; + boolean back = false; + float time = 0; + float s = 1f; + + @Override + public void simpleUpdate(float tpf) { + if (up) { + Vector3f v = l.getDirection(); + v.y += tpf / s; + setDir(v); + } + if (down) { + Vector3f v = l.getDirection(); + v.y -= tpf / s; + setDir(v); + } + if (right) { + Vector3f v = l.getDirection(); + v.x += tpf / s; + setDir(v); + } + if (left) { + Vector3f v = l.getDirection(); + v.x -= tpf / s; + setDir(v); + } + if (fwd) { + Vector3f v = l.getDirection(); + v.z += tpf / s; + setDir(v); + } + if (back) { + Vector3f v = l.getDirection(); + v.z -= tpf / s; + setDir(v); + } + + } + + private void setDir(Vector3f v) { + l.setDirection(v); + pssmFilter.setDirection(v); + pssmRenderer.setDirection(v); } } diff --git a/engine/src/test/jme3test/light/TestSpotLightShadows.java b/engine/src/test/jme3test/light/TestSpotLightShadows.java new file mode 100644 index 000000000..88f5db1c6 --- /dev/null +++ b/engine/src/test/jme3test/light/TestSpotLightShadows.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2009-2012 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.SpotLight; +import com.jme3.material.Material; +import com.jme3.math.*; +import com.jme3.post.FilterPostProcessor; +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.shader.VarType; +import com.jme3.shadow.CompareMode; +import com.jme3.shadow.EdgeFilteringMode; +import com.jme3.shadow.SpotLightShadowFilter; +import com.jme3.shadow.SpotLightShadowRenderer; +import com.jme3.texture.Texture.WrapMode; +import com.jme3.util.TangentBinormalGenerator; + +public class TestSpotLightShadows extends SimpleApplication { + + private Vector3f lightTarget = new Vector3f(12, 3.5f, 30); + + public static void main(String[] args) { + TestSpotLightShadows app = new TestSpotLightShadows(); + app.start(); + } + SpotLight spot; + Geometry lightMdl; + + public void setupLighting() { + AmbientLight al = new AmbientLight(); + al.setColor(ColorRGBA.White.mult(0.3f)); + rootNode.addLight(al); + + rootNode.setShadowMode(ShadowMode.CastAndReceive); + + spot = new SpotLight(); + + spot.setSpotRange(1000); + spot.setSpotInnerAngle(5f * FastMath.DEG_TO_RAD); + spot.setSpotOuterAngle(10 * FastMath.DEG_TO_RAD); + spot.setPosition(new Vector3f(70.70334f, 34.013165f, 27.1017f)); + spot.setDirection(lightTarget.subtract(spot.getPosition())); + spot.setColor(ColorRGBA.White.mult(2)); + rootNode.addLight(spot); + + +// PointLight pl=new PointLight(); +// pl.setPosition(new Vector3f(77.70334f, 34.013165f, 27.1017f)); +// pl.setRadius(1000); +// pl.setColor(ColorRGBA.White.mult(2)); +// rootNode.addLight(pl); + lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f)); + lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m")); + lightMdl.setLocalTranslation(new Vector3f(77.70334f, 34.013165f, 27.1017f)); + lightMdl.setLocalScale(5); + rootNode.attachChild(lightMdl); + +// DirectionalLight dl = new DirectionalLight(); +// dl.setDirection(lightTarget.subtract(new Vector3f(77.70334f, 34.013165f, 27.1017f))); +// dl.setColor(ColorRGBA.White.mult(0.7f)); +// rootNode.addLight(dl); + + + final SpotLightShadowRenderer slsr = new SpotLightShadowRenderer(assetManager, 512); + slsr.setLight(spot); + slsr.setShadowIntensity(0.5f); + slsr.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON); + viewPort.addProcessor(slsr); + + SpotLightShadowFilter slsf = new SpotLightShadowFilter(assetManager, 512); + slsf.setLight(spot); + slsf.setShadowIntensity(0.5f); + slsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON); + slsf.setEnabled(false); + + FilterPostProcessor fpp = new FilterPostProcessor(assetManager); + fpp.addFilter(slsf); + viewPort.addProcessor(fpp); + + ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, slsr, slsf, guiNode, inputManager, viewPort); + + inputManager.addListener(new ActionListener() { + public void onAction(String name, boolean isPressed, float tpf) { + if (name.equals("stop") && isPressed) { + stop = !stop; + // slsr.displayFrustum(); + System.out.println("pos : " + spot.getPosition()); + System.out.println("dir : " + spot.getDirection()); + } + } + }, "stop"); + + inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_1)); + + } + + public void setupFloor() { + Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); + mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat); + mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat); + mat.setBoolean("UseMaterialColors", true); + mat.setColor("Diffuse", ColorRGBA.White.clone()); + mat.setColor("Ambient", ColorRGBA.White.clone()); + // mat.setColor("Specular", ColorRGBA.White.clone()); + // mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat); + mat.setFloat("Shininess", 0); + // mat.setBoolean("VertexLighting", true); + + + Box floor = new Box(Vector3f.ZERO, 50, 1f, 50); + TangentBinormalGenerator.generate(floor); + floor.scaleTextureCoordinates(new Vector2f(5, 5)); + Geometry floorGeom = new Geometry("Floor", floor); + floorGeom.setMaterial(mat); + floorGeom.setShadowMode(ShadowMode.Receive); + rootNode.attachChild(floorGeom); + } + + public void setupSignpost() { + Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml"); + Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"); + // mat.setBoolean("VertexLighting", true); + signpost.setMaterial(mat); + signpost.rotate(0, FastMath.HALF_PI, 0); + signpost.setLocalTranslation(12, 3.5f, 30); + signpost.setLocalScale(4); + signpost.setShadowMode(ShadowMode.CastAndReceive); + TangentBinormalGenerator.generate(signpost); + rootNode.attachChild(signpost); + } + + @Override + public void simpleInitApp() { + cam.setLocation(new Vector3f(27.492603f, 29.138166f, -13.232513f)); + cam.setRotation(new Quaternion(0.25168246f, -0.10547892f, 0.02760565f, 0.96164864f)); + flyCam.setMoveSpeed(30); + + setupLighting(); + setupFloor(); + setupSignpost(); + + + } + float angle; + boolean stop = true; + + @Override + public void simpleUpdate(float tpf) { + if (!stop) { + super.simpleUpdate(tpf); + angle += tpf; + angle %= FastMath.TWO_PI; + + spot.setPosition(new Vector3f(FastMath.cos(angle) * 30f, 34.013165f, FastMath.sin(angle) * 30f)); + lightMdl.setLocalTranslation(spot.getPosition()); + spot.setDirection(lightTarget.subtract(spot.getPosition())); + } + } +}