From dff4befafb3ea75f57fadb18c94f4fd3b8edb6d1 Mon Sep 17 00:00:00 2001 From: Nehon Date: Thu, 17 Mar 2016 17:58:41 +0100 Subject: [PATCH] Added an option to not render backfaces shadows with the shadow renderer and the shadow filter. It's the default for the renderer but not for the filter as it may have some edges artifacts. --- .../com/jme3/shadow/AbstractShadowFilter.java | 47 +++++++- .../jme3/shadow/AbstractShadowRenderer.java | 104 ++++++++++++++---- .../DirectionalLightShadowRenderer.java | 2 + .../Common/MatDefs/Light/Lighting.j3md | 13 +-- .../Common/MatDefs/Misc/Unshaded.j3md | 4 + .../Common/MatDefs/Shadow/PostShadow.frag | 17 ++- .../Common/MatDefs/Shadow/PostShadow.j3md | 4 + .../Common/MatDefs/Shadow/PostShadow.vert | 32 ++++-- .../MatDefs/Shadow/PostShadowFilter.frag | 29 +++++ .../MatDefs/Shadow/PostShadowFilter.j3md | 10 +- .../MatDefs/Shadow/PostShadowFilter15.frag | 48 ++++++-- .../light/TestDirectionalLightShadow.java | 39 +++++-- .../jme3test/light/TestPointLightShadows.java | 27 ++++- 13 files changed, 312 insertions(+), 64 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java index 46dc8390e..f882ac580 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java +++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java @@ -37,6 +37,7 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.material.Material; +import com.jme3.material.RenderState; import com.jme3.math.Matrix4f; import com.jme3.math.Vector4f; import com.jme3.post.Filter; @@ -44,6 +45,7 @@ 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; /** @@ -74,6 +76,9 @@ public abstract class AbstractShadowFilter ext material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); this.shadowRenderer = shadowRenderer; this.shadowRenderer.setPostShadowMaterial(material); + + //this is legacy setting for shadows with backface shadows + this.shadowRenderer.setRenderBackFacesShadows(true); } @Override @@ -126,7 +131,7 @@ public abstract class AbstractShadowFilter ext /** * How far the shadows are rendered in the view * - * @see setShadowZExtend(float zFar) + * @see #setShadowZExtend(float zFar) * @return shadowZExtend */ public float getShadowZExtend() { @@ -248,6 +253,46 @@ public abstract class AbstractShadowFilter ext shadowRenderer.setEdgeFilteringMode(filterMode); } + /** + * + * !! WARNING !! this parameter is defaulted to true for the ShadowFilter. + * Setting it to true, may produce edges artifacts on shadows. * + * + * Set to true if you want back faces shadows on geometries. + * Note that back faces shadows will be blended over dark lighten areas and may produce overly dark lighting. + * + * Setting this parameter will override this parameter for ALL materials in the scene. + * This also will automatically adjust the faceCullMode and the PolyOffset of the pre shadow pass. + * You can modify them by using {@link #getPreShadowForcedRenderState()} + * + * If you want to set it differently for each material in the scene you have to use the ShadowRenderer instead + * of the shadow filter. + * + * @param renderBackFacesShadows true or false. + */ + public void setRenderBackFacesShadows(Boolean renderBackFacesShadows) { + shadowRenderer.setRenderBackFacesShadows(renderBackFacesShadows); + } + + /** + * if this filter renders back faces shadows + * @return true if this filter renders back faces shadows + */ + public boolean isRenderBackFacesShadows() { + return shadowRenderer.isRenderBackFacesShadows(); + } + + /** + * returns the pre shadows pass render state. + * use it to adjust the RenderState parameters of the pre shadow pass. + * Note that this will be overriden if the preShadow technique in the material has a ForcedRenderState + * @return the pre shadow render state. + */ + public RenderState getPreShadowForcedRenderState() { + return shadowRenderer.getPreShadowForcedRenderState(); + } + + /** * returns the the edge filtering mode * diff --git a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java index 07698f747..b55c6b2d2 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java @@ -31,10 +31,6 @@ */ package com.jme3.shadow; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - import com.jme3.asset.AssetManager; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; @@ -42,6 +38,7 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.export.Savable; import com.jme3.material.Material; +import com.jme3.material.RenderState; import com.jme3.math.ColorRGBA; import com.jme3.math.Matrix4f; import com.jme3.math.Vector2f; @@ -67,6 +64,10 @@ import com.jme3.texture.Texture.ShadowCompareMode; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + /** * abstract shadow renderer that holds commons feature to have for a shadow * renderer @@ -92,6 +93,8 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable protected EdgeFilteringMode edgeFilteringMode = EdgeFilteringMode.Bilinear; protected CompareMode shadowCompareMode = CompareMode.Hardware; protected Picture[] dispPic; + protected RenderState forcedRenderState = new RenderState(); + protected Boolean renderBackFacesShadows; /** * true if the fallback material should be used, otherwise false @@ -182,6 +185,14 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable setShadowCompareMode(shadowCompareMode); setEdgeFilteringMode(edgeFilteringMode); setShadowIntensity(shadowIntensity); + initForcedRenderState(); + } + + protected void initForcedRenderState() { + forcedRenderState.setFaceCullMode(RenderState.FaceCullMode.Front); + forcedRenderState.setColorWrite(false); + forcedRenderState.setDepthWrite(true); + forcedRenderState.setDepthTest(true); } /** @@ -357,9 +368,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable * 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 receivers of the whole scene - * @param shadowMapOcculders + * @param shadowMapOccluders the list of occluders * @return */ protected abstract GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders); @@ -426,9 +435,11 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable renderManager.getRenderer().setFrameBuffer(shadowFB[shadowMapIndex]); renderManager.getRenderer().clearBuffers(true, true, true); + renderManager.setForcedRenderState(forcedRenderState); // render shadow casters to shadow map viewPort.getQueue().renderShadowQueue(shadowMapOccluders, renderManager, shadowCam, true); + renderManager.setForcedRenderState(null); } boolean debugfrustums = false; @@ -536,18 +547,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable private void setMatParams(GeometryList l) { //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; - } - } + buildMatCache(l); //iterating through the mat cache and setting the parameters for (Material mat : matCache) { @@ -567,6 +567,10 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable if (fadeInfo != null) { mat.setVector2("FadeInfo", fadeInfo); } + if(renderBackFacesShadows != null){ + mat.setBoolean("BackfaceShadows", renderBackFacesShadows); + } + setMaterialParameters(mat); } @@ -578,6 +582,21 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } + private void buildMatCache(GeometryList l) { + 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; + } + } + } + /** * for internal use only */ @@ -588,7 +607,10 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable postshadowMat.setTexture(shadowMapStringCache[j], shadowMaps[j]); } if (fadeInfo != null) { - postshadowMat.setVector2("FadeInfo", fadeInfo); + postshadowMat.setVector2("FadeInfo", fadeInfo); + } + if(renderBackFacesShadows != null){ + postshadowMat.setBoolean("BackfaceShadows", renderBackFacesShadows); } } @@ -731,6 +753,48 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable @Deprecated public void setFlushQueues(boolean flushQueues) {} + + /** + * returns the pre shadows pass render state. + * use it to adjust the RenderState parameters of the pre shadow pass. + * Note that this will be overriden if the preShadow technique in the material has a ForcedRenderState + * @return the pre shadow render state. + */ + public RenderState getPreShadowForcedRenderState() { + return forcedRenderState; + } + + /** + * Set to true if you want back faces shadows on geometries. + * Note that back faces shadows will be blended over dark lighten areas and may produce overly dark lighting. + * + * Also note that setting this parameter will override this parameter for ALL materials in the scene. + * You can alternatively change this parameter on a single material using {@link Material#setBoolean(String, boolean)} + * + * This also will automatically adjust the faceCullMode and the PolyOffset of the pre shadow pass. + * You can modify them by using {@link #getPreShadowForcedRenderState()} + * + * @param renderBackFacesShadows true or false. + */ + public void setRenderBackFacesShadows(Boolean renderBackFacesShadows) { + this.renderBackFacesShadows = renderBackFacesShadows; + if(renderBackFacesShadows) { + getPreShadowForcedRenderState().setPolyOffset(5, 3); + getPreShadowForcedRenderState().setFaceCullMode(RenderState.FaceCullMode.Back); + }else{ + getPreShadowForcedRenderState().setPolyOffset(0, 0); + getPreShadowForcedRenderState().setFaceCullMode(RenderState.FaceCullMode.Front); + } + } + + /** + * if this processor renders back faces shadows + * @return true if this processor renders back faces shadows + */ + public boolean isRenderBackFacesShadows() { + return renderBackFacesShadows != null?renderBackFacesShadows:false; + } + /** * De-serialize this instance, for example when loading from a J3O file. * diff --git a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java index acf8a9677..33adf1c09 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java @@ -215,6 +215,7 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer { @Override protected void setMaterialParameters(Material material) { material.setColor("Splits", splits); + material.setVector3("LightDir", light.getDirection()); if (fadeInfo != null) { material.setVector2("FadeInfo", fadeInfo); } @@ -224,6 +225,7 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer { protected void clearMaterialParameters(Material material) { material.clearParam("Splits"); material.clearParam("FadeInfo"); + material.clearParam("LightDir"); } /** diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md index 1ea351f61..a0f31aeb0 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md @@ -113,6 +113,8 @@ MaterialDef Phong Lighting { //For instancing Boolean UseInstancing + + Boolean BackfaceShadows: false } Technique { @@ -213,14 +215,6 @@ MaterialDef Phong Lighting { INSTANCING : UseInstancing } - ForcedRenderState { - FaceCull Off - DepthTest On - DepthWrite On - PolyOffset 5 3 - ColorWrite Off - } - } @@ -233,6 +227,7 @@ MaterialDef Phong Lighting { WorldMatrix ViewProjectionMatrix ViewMatrix + NormalMatrix } Defines { @@ -247,6 +242,7 @@ MaterialDef Phong Lighting { POINTLIGHT : LightViewProjectionMatrix5 NUM_BONES : NumberOfBones INSTANCING : UseInstancing + BACKFACE_SHADOWS: BackfaceShadows } ForcedRenderState { @@ -265,6 +261,7 @@ MaterialDef Phong Lighting { WorldMatrix ViewProjectionMatrix ViewMatrix + NormalMatrix } Defines { diff --git a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md index 0831c3fe5..68f07f98d 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md @@ -51,6 +51,8 @@ MaterialDef Unshaded { Float PCFEdge Float ShadowMapSize + + Boolean BackfaceShadows: true } Technique { @@ -169,6 +171,7 @@ MaterialDef Unshaded { POINTLIGHT : LightViewProjectionMatrix5 NUM_BONES : NumberOfBones INSTANCING : UseInstancing + BACKFACE_SHADOWS: BackfaceShadows } ForcedRenderState { @@ -201,6 +204,7 @@ MaterialDef Unshaded { POINTLIGHT : LightViewProjectionMatrix5 NUM_BONES : NumberOfBones INSTANCING : UseInstancing + BACKFACE_SHADOWS: BackfaceShadows } ForcedRenderState { diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.frag b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.frag index 52bc6e9e4..a2d191895 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.frag @@ -9,6 +9,9 @@ varying vec4 projCoord0; varying vec4 projCoord1; varying vec4 projCoord2; varying vec4 projCoord3; +#ifndef BACKFACE_SHADOWS + varying float nDotL; +#endif #ifdef POINTLIGHT varying vec4 projCoord4; @@ -46,9 +49,15 @@ void main(){ if(alpha<=m_AlphaDiscardThreshold){ discard; } + #endif + #ifndef BACKFACE_SHADOWS + if(nDotL > 0.0){ + discard; + } #endif - + + float shadow = 1.0; #ifdef POINTLIGHT @@ -71,11 +80,11 @@ void main(){ #endif #ifdef FADE - shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); + 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); + shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); + gl_FragColor = vec4(shadow, shadow, shadow, 1.0); } diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md index a6e1dfef7..928637adf 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md @@ -29,6 +29,8 @@ MaterialDef Post Shadow { Float PCFEdge Float ShadowMapSize + + Boolean BackfaceShadows: false } @@ -49,6 +51,7 @@ MaterialDef Post Shadow { FADE : FadeInfo PSSM : Splits POINTLIGHT : LightViewProjectionMatrix5 + BACKFACE_SHADOWS: BackfaceShadows } RenderState { @@ -75,6 +78,7 @@ MaterialDef Post Shadow { FADE : FadeInfo PSSM : Splits POINTLIGHT : LightViewProjectionMatrix5 + BACKFACE_SHADOWS: BackfaceShadows } RenderState { diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.vert b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.vert index e56e10e55..de4490820 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.vert @@ -7,7 +7,6 @@ uniform mat4 m_LightViewProjectionMatrix1; uniform mat4 m_LightViewProjectionMatrix2; uniform mat4 m_LightViewProjectionMatrix3; -uniform vec3 m_LightPos; varying vec4 projCoord0; varying vec4 projCoord1; @@ -17,12 +16,14 @@ varying vec4 projCoord3; #ifdef POINTLIGHT uniform mat4 m_LightViewProjectionMatrix4; uniform mat4 m_LightViewProjectionMatrix5; + uniform vec3 m_LightPos; varying vec4 projCoord4; varying vec4 projCoord5; varying vec4 worldPos; #else + uniform vec3 m_LightDir; #ifndef PSSM - uniform vec3 m_LightDir; + uniform vec3 m_LightPos; varying float lightDot; #endif #endif @@ -30,12 +31,15 @@ varying vec4 projCoord3; #if defined(PSSM) || defined(FADE) varying float shadowPosition; #endif -varying vec3 lightVec; varying vec2 texCoord; - attribute vec3 inPosition; +#ifndef BACKFACE_SHADOWS + attribute vec3 inNormal; + varying float nDotL; +#endif + #ifdef DISCARD_ALPHA attribute vec2 inTexCoord; #endif @@ -53,16 +57,17 @@ void main(){ Skinning_Compute(modelSpacePos); #endif gl_Position = TransformWorldViewProjection(modelSpacePos); + vec3 lightDir; #if defined(PSSM) || defined(FADE) - shadowPosition = gl_Position.z; + shadowPosition = gl_Position.z; #endif #ifndef POINTLIGHT vec4 worldPos=vec4(0.0); #endif // get the vertex in world space - worldPos = g_WorldMatrix * modelSpacePos; + worldPos = TransformWorld(modelSpacePos); #ifdef DISCARD_ALPHA texCoord = inTexCoord; @@ -77,8 +82,21 @@ void main(){ projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos; #else #ifndef PSSM - vec3 lightDir = worldPos.xyz - m_LightPos; + //Spot light + lightDir = worldPos.xyz - m_LightPos; lightDot = dot(m_LightDir,lightDir); #endif #endif + + #ifndef BACKFACE_SHADOWS + vec3 normal = normalize(TransformWorld(vec4(inNormal,0.0))).xyz; + #ifdef POINTLIGHT + lightDir = worldPos.xyz - m_LightPos; + #else + #ifdef PSSM + lightDir = m_LightDir; + #endif + #endif + nDotL = dot(normal, lightDir); + #endif } \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.frag b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.frag index 74e59482b..c7edc7951 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.frag @@ -18,6 +18,8 @@ uniform mat4 m_LightViewProjectionMatrix1; uniform mat4 m_LightViewProjectionMatrix2; uniform mat4 m_LightViewProjectionMatrix3; +uniform vec2 g_ResolutionInverse; + #ifdef POINTLIGHT uniform vec3 m_LightPos; uniform mat4 m_LightViewProjectionMatrix4; @@ -39,6 +41,19 @@ vec3 getPosition(in float depth, in vec2 uv){ return pos.xyz / pos.w; } +vec3 approximateNormal(in vec4 worldPos,in vec2 texCoord){ + float step = g_ResolutionInverse.x ; + float stepy = g_ResolutionInverse.y ; + float depth2 = texture2D(m_DepthTexture,texCoord + vec2(step,-stepy)).r; + float depth3 = texture2D(m_DepthTexture,texCoord + vec2(-step,-stepy)).r; + vec4 worldPos2 = vec4(getPosition(depth2,texCoord + vec2(step,-stepy)),1.0); + vec4 worldPos3 = vec4(getPosition(depth3,texCoord + vec2(-step,-stepy)),1.0); + + vec3 v1 = (worldPos - worldPos2).xyz; + vec3 v2 = (worldPos3 - worldPos2).xyz; + return normalize(cross(v1, v2)); +} + void main(){ #if !defined( RENDER_SHADOWS ) gl_FragColor = texture2D(m_Texture,texCoord); @@ -48,6 +63,7 @@ void main(){ float depth = texture2D(m_DepthTexture,texCoord).r; vec4 color = texture2D(m_Texture,texCoord); + //Discard shadow computation on the sky if(depth == 1.0){ gl_FragColor = color; @@ -56,6 +72,19 @@ void main(){ // get the vertex in world space vec4 worldPos = vec4(getPosition(depth,texCoord),1.0); + vec3 normal = approximateNormal(worldPos, texCoord); + + vec3 lightDir; + #ifdef PSSM + lightDir = m_LightDir; + #else + lightDir = worldPos.xyz - m_LightPos; + #endif + float ndotl = dot(normal, lightDir); + if(ndotl > -0.0){ + gl_FragColor = color; + return; + } #if (!defined(POINTLIGHT) && !defined(PSSM)) vec3 lightDir = worldPos.xyz - m_LightPos; diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md index 9a78752ae..ced2f9fdb 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md @@ -38,13 +38,15 @@ MaterialDef Post Shadow { Texture2D Texture Texture2D DepthTexture + Boolean BackfaceShadows: true } Technique { VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.vert FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag - WorldParameters { + WorldParameters { + ResolutionInverse } Defines { @@ -59,7 +61,7 @@ MaterialDef Post Shadow { POINTLIGHT : LightViewProjectionMatrix5 //if no shadow map don't render shadows RENDER_SHADOWS : ShadowMap0 - + BACKFACE_SHADOWS : BackfaceShadows } } @@ -68,7 +70,8 @@ MaterialDef Post Shadow { VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowFilter.vert FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowFilter.frag - WorldParameters { + WorldParameters { + ResolutionInverse } Defines { @@ -79,6 +82,7 @@ MaterialDef Post Shadow { FADE : FadeInfo PSSM : Splits POINTLIGHT : LightViewProjectionMatrix5 + BACKFACE_SHADOWS : BackfaceShadows } } diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter15.frag b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter15.frag index d19e9aef4..054382cd6 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter15.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter15.frag @@ -20,14 +20,16 @@ uniform mat4 m_LightViewProjectionMatrix1; uniform mat4 m_LightViewProjectionMatrix2; uniform mat4 m_LightViewProjectionMatrix3; +uniform vec2 g_ResolutionInverse; + #ifdef POINTLIGHT uniform vec3 m_LightPos; uniform mat4 m_LightViewProjectionMatrix4; uniform mat4 m_LightViewProjectionMatrix5; #else + uniform vec3 m_LightDir; #ifndef PSSM - uniform vec3 m_LightPos; - uniform vec3 m_LightDir; + uniform vec3 m_LightPos; #endif #endif @@ -41,6 +43,23 @@ vec3 getPosition(in float depth, in vec2 uv){ return pos.xyz / pos.w; } +#ifndef BACKFACE_SHADOWS + vec3 approximateNormal(in float depth,in vec4 worldPos,in vec2 texCoord, in int numSample){ + float step = g_ResolutionInverse.x ; + float stepy = g_ResolutionInverse.y ; + float depth1 = fetchTextureSample(m_DepthTexture,texCoord + vec2(-step,stepy),numSample).r; + float depth2 = fetchTextureSample(m_DepthTexture,texCoord + vec2(step,stepy),numSample).r; + vec3 v1, v2; + vec4 worldPos1 = vec4(getPosition(depth1,texCoord + vec2(-step,stepy)),1.0); + vec4 worldPos2 = vec4(getPosition(depth2,texCoord + vec2(step,stepy)),1.0); + + v1 = normalize((worldPos1 - worldPos)).xyz; + v2 = normalize((worldPos2 - worldPos)).xyz; + return normalize(cross(v2, v1)); + + } +#endif + vec4 main_multiSample(in int numSample){ float depth = fetchTextureSample(m_DepthTexture,texCoord,numSample).r;//getDepth(m_DepthTexture,texCoord).r; vec4 color = fetchTextureSample(m_Texture,texCoord,numSample); @@ -52,12 +71,27 @@ vec4 main_multiSample(in int numSample){ // get the vertex in world space vec4 worldPos = vec4(getPosition(depth,texCoord),1.0); - + + + vec3 lightDir; + #ifdef PSSM + lightDir = m_LightDir; + #else + lightDir = worldPos.xyz - m_LightPos; + #endif + + #ifndef BACKFACE_SHADOWS + vec3 normal = approximateNormal(depth, worldPos, texCoord, numSample); + float ndotl = dot(normal, lightDir); + if(ndotl > 0.0){ + return color; + } + #endif + #if (!defined(POINTLIGHT) && !defined(PSSM)) - vec3 lightDir = worldPos.xyz - m_LightPos; - if( dot(m_LightDir,lightDir)<0){ - return color; - } + if( dot(m_LightDir,lightDir)<0){ + return color; + } #endif // populate the light view matrices array and convert vertex to light viewProj space diff --git a/jme3-examples/src/main/java/jme3test/light/TestDirectionalLightShadow.java b/jme3-examples/src/main/java/jme3test/light/TestDirectionalLightShadow.java index eaf516fae..14f220374 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestDirectionalLightShadow.java +++ b/jme3-examples/src/main/java/jme3test/light/TestDirectionalLightShadow.java @@ -40,12 +40,14 @@ import com.jme3.input.controls.KeyTrigger; import com.jme3.light.AmbientLight; import com.jme3.light.DirectionalLight; import com.jme3.material.Material; +import com.jme3.material.RenderState; 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.post.ssao.SSAOFilter; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; @@ -69,6 +71,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act private Geometry ground; private Material matGroundU; private Material matGroundL; + private AmbientLight al; public static void main(String[] args) { TestDirectionalLightShadow app = new TestDirectionalLightShadow(); @@ -99,7 +102,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act 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("Ambient", ColorRGBA.White); mat[1].setColor("Diffuse", ColorRGBA.White.clone()); @@ -110,9 +113,14 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act TangentBinormalGenerator.generate(obj[1]); TangentBinormalGenerator.generate(obj[0]); + Spatial t = obj[0].clone(false); + t.setLocalScale(10f); + t.setMaterial(mat[1]); + rootNode.attachChild(t); + t.setLocalTranslation(0, 25, 0); for (int i = 0; i < 60; i++) { - Spatial t = obj[FastMath.nextRandomInt(0, obj.length - 1)].clone(false); + 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); @@ -142,8 +150,8 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act rootNode.addLight(l); - AmbientLight al = new AmbientLight(); - al.setColor(ColorRGBA.White.mult(0.5f)); + al = new AmbientLight(); + al.setColor(ColorRGBA.White.mult(0.02f)); rootNode.addLight(al); Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false); @@ -156,8 +164,11 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act @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)); +// cam.setLocation(new Vector3f(65.25412f, 44.38738f, 9.087874f)); +// cam.setRotation(new Quaternion(0.078139365f, 0.050241485f, -0.003942559f, 0.9956679f)); + + cam.setLocation(new Vector3f(3.3720117f, 42.838284f, -83.43792f)); + cam.setRotation(new Quaternion(0.13833192f, -0.08969371f, 0.012581267f, 0.9862358f)); flyCam.setMoveSpeed(100); @@ -166,7 +177,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act dlsr = new DirectionalLightShadowRenderer(assetManager, SHADOWMAP_SIZE, 3); dlsr.setLight(l); dlsr.setLambda(0.55f); - dlsr.setShadowIntensity(0.6f); + dlsr.setShadowIntensity(0.8f); dlsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest); dlsr.displayDebug(); viewPort.addProcessor(dlsr); @@ -174,7 +185,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act dlsf = new DirectionalLightShadowFilter(assetManager, SHADOWMAP_SIZE, 3); dlsf.setLight(l); dlsf.setLambda(0.55f); - dlsf.setShadowIntensity(0.6f); + dlsf.setShadowIntensity(0.8f); dlsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest); dlsf.setEnabled(false); @@ -205,10 +216,11 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act inputManager.addMapping("fwd", new KeyTrigger(KeyInput.KEY_PGUP)); inputManager.addMapping("back", new KeyTrigger(KeyInput.KEY_PGDN)); inputManager.addMapping("pp", new KeyTrigger(KeyInput.KEY_P)); + inputManager.addMapping("backShadows", new KeyTrigger(KeyInput.KEY_B)); inputManager.addListener(this, "lambdaUp", "lambdaDown", "ThicknessUp", "ThicknessDown", - "switchGroundMat", "debug", "up", "down", "right", "left", "fwd", "back", "pp", "stabilize", "distance"); + "switchGroundMat", "debug", "up", "down", "right", "left", "fwd", "back", "pp", "stabilize", "distance", "ShadowUp", "ShadowDown", "backShadows"); ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, dlsr, dlsf, guiNode, inputManager, viewPort); @@ -255,12 +267,19 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act dlsf.setLambda(dlsr.getLambda() - 0.01f); System.out.println("Lambda : " + dlsr.getLambda()); } - + if ((name.equals("ShadowUp") || name.equals("ShadowDown")) && keyPressed) { + al.setColor(ColorRGBA.White.mult((1 - dlsr.getShadowIntensity()) * 0.2f)); + } if (name.equals("debug") && keyPressed) { dlsr.displayFrustum(); } + if (name.equals("backShadows") && keyPressed) { + dlsr.setRenderBackFacesShadows(!dlsr.isRenderBackFacesShadows()); + dlsf.setRenderBackFacesShadows(!dlsf.isRenderBackFacesShadows()); + } + if (name.equals("stabilize") && keyPressed) { dlsr.setEnabledStabilization(!dlsr.isEnabledStabilization()); dlsf.setEnabledStabilization(!dlsf.isEnabledStabilization()); diff --git a/jme3-examples/src/main/java/jme3test/light/TestPointLightShadows.java b/jme3-examples/src/main/java/jme3test/light/TestPointLightShadows.java index a98ac03db..b5ec1372e 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestPointLightShadows.java +++ b/jme3-examples/src/main/java/jme3test/light/TestPointLightShadows.java @@ -32,7 +32,10 @@ package jme3test.light; import com.jme3.app.SimpleApplication; +import com.jme3.input.controls.ActionListener; +import com.jme3.light.AmbientLight; import com.jme3.light.PointLight; +import com.jme3.math.ColorRGBA; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; import com.jme3.post.FilterPostProcessor; @@ -45,7 +48,7 @@ import com.jme3.shadow.EdgeFilteringMode; import com.jme3.shadow.PointLightShadowFilter; import com.jme3.shadow.PointLightShadowRenderer; -public class TestPointLightShadows extends SimpleApplication { +public class TestPointLightShadows extends SimpleApplication implements ActionListener{ public static final int SHADOWMAP_SIZE = 512; public static void main(String[] args) { @@ -55,15 +58,21 @@ public class TestPointLightShadows extends SimpleApplication { Node lightNode; PointLightShadowRenderer plsr; PointLightShadowFilter plsf; + AmbientLight al; @Override - public void simpleInitApp() { + public void simpleInitApp () { flyCam.setMoveSpeed(10); cam.setLocation(new Vector3f(0.040581334f, 1.7745866f, 6.155161f)); cam.setRotation(new Quaternion(4.3868728E-5f, 0.9999293f, -0.011230096f, 0.0039059948f)); + al = new AmbientLight(ColorRGBA.White.mult(0.02f)); + rootNode.addLight(al); - Node scene = (Node) assetManager.loadModel("Models/Test/CornellBox.j3o"); + + + + Node scene = (Node) assetManager.loadModel("Models/Test/CornellBox_1.j3o"); scene.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); rootNode.attachChild(scene); rootNode.getChild("Cube").setShadowMode(RenderQueue.ShadowMode.Receive); @@ -89,6 +98,7 @@ public class TestPointLightShadows extends SimpleApplication { plsr.setEdgeFilteringMode(EdgeFilteringMode.PCF4); plsr.setShadowZExtend(15); plsr.setShadowZFadeLength(5); + plsr.setShadowIntensity(0.9f); // plsr.setFlushQueues(false); //plsr.displayFrustum(); plsr.displayDebug(); @@ -99,18 +109,27 @@ public class TestPointLightShadows extends SimpleApplication { plsf.setLight((PointLight) scene.getLocalLightList().get(0)); plsf.setShadowZExtend(15); plsf.setShadowZFadeLength(5); + plsf.setShadowIntensity(0.8f); plsf.setEdgeFilteringMode(EdgeFilteringMode.PCF4); plsf.setEnabled(false); FilterPostProcessor fpp = new FilterPostProcessor(assetManager); fpp.addFilter(plsf); viewPort.addProcessor(fpp); - + inputManager.addListener(this,"ShadowUp","ShadowDown"); ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, plsr, plsf, guiNode, inputManager, viewPort); + } @Override public void simpleUpdate(float tpf) { // lightNode.move(FastMath.cos(tpf) * 0.4f, 0, FastMath.sin(tpf) * 0.4f); } + + @Override + public void onAction(String name, boolean isPressed, float tpf) { + if ((name.equals("ShadowUp") || name.equals("ShadowDown")) && isPressed) { + al.setColor(ColorRGBA.White.mult((1 - plsr.getShadowIntensity()) * 0.2f)); + } + } } \ No newline at end of file