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