From bf55974bf40f688f43c5f2135ad654784f6bd287 Mon Sep 17 00:00:00 2001 From: "rem..om" Date: Sat, 7 Apr 2012 19:55:43 +0000 Subject: [PATCH] Changed the way post shadow pass is done. It's now a technique of the lighting material definition. This allow to have shadows that fully works with partially transparent objects (like trees). If a material does not have the postShadow technique, the renderer uses the fallback material (like before). git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9279 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../Common/MatDefs/Light/Lighting.j3md | 69 +++++++++++++++- .../Common/MatDefs/Shadow/PostShadowPSSM.frag | 50 ++++++++++-- .../Common/MatDefs/Shadow/PostShadowPSSM.vert | 9 ++- .../MatDefs/Shadow/PostShadowPSSM15.frag | 24 +++++- .../Common/MatDefs/Shadow/PreShadow.frag | 22 ++++-- .../com/jme3/shadow/PssmShadowRenderer.java | 79 +++++++++++++++++-- .../jme3test/light/TestTransparentShadow.java | 57 +++++++------ 7 files changed, 258 insertions(+), 52 deletions(-) diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md index 95042b7c1..5810b2039 100644 --- a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md +++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md @@ -99,6 +99,25 @@ MaterialDef Phong Lighting { // the env map is a spheremap and not a cube map Boolean EnvMapAsSphereMap + + //shadows + Int FilterMode + Boolean HardwareShadows + + Texture2D ShadowMap0 + Texture2D ShadowMap1 + Texture2D ShadowMap2 + Texture2D ShadowMap3 + + Float ShadowIntensity + Vector4 Splits + + Matrix4 LightViewProjectionMatrix0 + Matrix4 LightViewProjectionMatrix1 + Matrix4 LightViewProjectionMatrix2 + Matrix4 LightViewProjectionMatrix3 + + Float PCFEdge } Technique { @@ -141,7 +160,7 @@ MaterialDef Phong Lighting { SEPARATE_TEXCOORD : SeparateTexCoord USE_REFLECTION : EnvMap - SPHERE_MAP : SphereMap + SPHERE_MAP : SphereMap } } @@ -156,7 +175,8 @@ MaterialDef Phong Lighting { } Defines { - DIFFUSEMAP_ALPHA : DiffuseMap + COLOR_MAP : ColorMap + DISCARD_ALPHA : AlphaDiscardThreshold } RenderState { @@ -169,6 +189,51 @@ MaterialDef Phong Lighting { } + + Technique PostShadow15{ + 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 + DISCARD_ALPHA : AlphaDiscardThreshold + COLOR_MAP : ColorMap + } + + RenderState { + Blend Modulate + } + } + + Technique PostShadow{ + 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 + DISCARD_ALPHA : AlphaDiscardThreshold + COLOR_MAP : ColorMap + } + + RenderState { + Blend Modulate + } + } + Technique PreNormalPass { VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag index ab5993b6b..cb6a6a5ff 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag @@ -1,6 +1,6 @@ #ifdef HARDWARE_SHADOWS #define SHADOWMAP sampler2DShadow - #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r + #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r #else #define SHADOWMAP sampler2D #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r) @@ -27,6 +27,7 @@ #define KERNEL 8.0 #endif + uniform SHADOWMAP m_ShadowMap0; uniform SHADOWMAP m_ShadowMap1; uniform SHADOWMAP m_ShadowMap2; @@ -43,7 +44,7 @@ varying vec4 projCoord3; varying float shadowPosition; -const float texSize = 1024.0; +float texSize = 1024.0; const float pixSize = 1.0 / texSize; const vec2 pixSize2 = vec2(pixSize); @@ -64,6 +65,11 @@ float Shadow_BorderCheck(in vec2 coord){ } float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ + float border = Shadow_BorderCheck(projCoord.xy); + if (border > 0.0) + return 1.0; + + float shadow = 0.0; vec2 o = mod(floor(gl_FragCoord.xy), 2.0); shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, 1.5) + o); @@ -75,6 +81,9 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ } float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ + float border = Shadow_BorderCheck(projCoord.xy); + if (border > 0.0) + return 1.0; vec4 gather = vec4(0.0); gather.x = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 0.0)); gather.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0)); @@ -88,12 +97,15 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ float shadow = 0.0; + float border = Shadow_BorderCheck(projCoord.xy); + if (border > 0.0) + return 1.0; float bound = KERNEL * 0.5 - 0.5; bound *= PCFEDGE; for (float y = -bound; y <= bound; y += PCFEDGE){ for (float x = -bound; x <= bound; x += PCFEDGE){ shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) + - Shadow_BorderCheck(projCoord.xy), + border, 0.0, 1.0); } } @@ -102,18 +114,46 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ return shadow; } -void main(){ +#ifdef DISCARD_ALPHA + #ifdef COLOR_MAP + uniform sampler2D m_ColorMap; + #else + uniform sampler2D m_DiffuseMap; + #endif + uniform float m_AlphaDiscardThreshold; + varying vec2 texCoord; +#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 + + vec4 shadowPerSplit = vec4(0.0); shadowPerSplit.x = GETSHADOW(m_ShadowMap0, projCoord0); shadowPerSplit.y = GETSHADOW(m_ShadowMap1, projCoord1); shadowPerSplit.z = GETSHADOW(m_ShadowMap2, projCoord2); shadowPerSplit.w = GETSHADOW(m_ShadowMap3, projCoord3); + vec4 less = step( shadowPosition, m_Splits ); vec4 more = vec4(1.0) - step( shadowPosition, vec4(0.0, m_Splits.xyz) ); float shadow = dot(shadowPerSplit, less * more ); shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); - gl_FragColor = vec4(shadow, shadow, shadow, 1.0); + + + gl_FragColor = vec4(shadow, shadow, shadow, 1.0); + //gl_FragColor = vec4(alpha, alpha, alpha, 1.0); } diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert index b1c169042..c2c8823e3 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert @@ -13,7 +13,12 @@ varying vec4 projCoord3; varying float shadowPosition; +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, @@ -28,7 +33,9 @@ void main(){ // get the vertex in world space vec4 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; diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag index 0dc1ddfde..02ac3f68a 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag @@ -120,9 +120,31 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ return shadow; } +#ifdef DISCARD_ALPHA + #ifdef COLOR_MAP + uniform sampler2D m_ColorMap; + #else + uniform sampler2D m_DiffuseMap; + #endif + uniform float m_AlphaDiscardThreshold; + varying vec2 texCoord; +#endif + void main(){ float shadow = 0.0; + + #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 if(shadowPosition < m_Splits.x){ shadow = GETSHADOW(m_ShadowMap0, projCoord0); }else if( shadowPosition < m_Splits.y){ @@ -134,6 +156,6 @@ void main(){ } shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); - outFragColor = vec4(shadow, shadow, shadow, 1.0); + outFragColor = vec4(shadow, shadow, shadow, 1.0); } diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag index 5d957e908..080666a4e 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag @@ -1,14 +1,26 @@ varying vec2 texCoord; -#ifdef DIFFUSEMAP_ALPHA -uniform sampler2D m_DiffuseMap; +#ifdef DISCARD_ALPHA + #ifdef COLOR_MAP + uniform sampler2D m_ColorMap; + #else + uniform sampler2D m_DiffuseMap; + #endif + uniform float m_AlphaDiscardThreshold; #endif void main(){ - #ifdef DIFFUSEMAP_ALPHA - if (texture2D(m_DiffuseMap, texCoord).a <= 0.50) - discard; + #ifdef DISCARD_ALPHA + #ifdef COLOR_MAP + if (texture2D(m_ColorMap, texCoord).a <= m_AlphaDiscardThreshold){ + discard; + } + #else + if (texture2D(m_DiffuseMap, texCoord).a <= m_AlphaDiscardThreshold){ + discard; + } + #endif #endif gl_FragColor = vec4(1.0); diff --git a/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java b/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java index 9fa95cb4b..ff71038d8 100644 --- a/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java +++ b/engine/src/core/com/jme3/shadow/PssmShadowRenderer.java @@ -31,11 +31,13 @@ package com.jme3.shadow; import com.jme3.asset.AssetManager; import com.jme3.material.Material; +import com.jme3.material.RenderState; import com.jme3.math.ColorRGBA; import com.jme3.math.Matrix4f; import com.jme3.math.Vector3f; 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; @@ -140,6 +142,12 @@ public class PssmShadowRenderer implements SceneProcessor { private Picture[] dispPic; private Vector3f[] points = new Vector3f[8]; private boolean flushQueues = true; + //render state for post shadow pass + private RenderState state = new RenderState(); + // define if the fallback material should be used. + private boolean needsfallBackMaterial = false; + //Name of the post material technique + private String postTechniqueName = "PostShadow"; /** * Create a PSSM Shadow Renderer @@ -208,13 +216,17 @@ public class PssmShadowRenderer implements SceneProcessor { for (int i = 0; i < points.length; i++) { points[i] = new Vector3f(); } + + //initializing render state for post shadow pass (modulade blending and cullmode of for back faces ) + state.setBlendMode(RenderState.BlendMode.Modulate); + state.setFaceCullMode(RenderState.FaceCullMode.Off); } /** * Sets the filtering mode for shadow edges see {@link FilterMode} for more info * @param filterMode */ - public void setFilterMode(FilterMode filterMode) { + final public void setFilterMode(FilterMode filterMode) { if (filterMode == null) { throw new NullPointerException(); } @@ -243,7 +255,7 @@ public class PssmShadowRenderer implements SceneProcessor { * sets the shadow compare mode see {@link CompareMode} for more info * @param compareMode */ - public void setCompareMode(CompareMode compareMode) { + final public void setCompareMode(CompareMode compareMode) { if (compareMode == null) { throw new NullPointerException(); } @@ -306,6 +318,12 @@ public class PssmShadowRenderer implements SceneProcessor { 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() { @@ -430,15 +448,24 @@ public class PssmShadowRenderer implements SceneProcessor { public void postFrame(FrameBuffer out) { Camera cam = viewPort.getCamera(); if (!noOccluders) { - postshadowMat.setColor("Splits", splits); - for (int i = 0; i < nbSplits; i++) { - postshadowMat.setMatrix4("LightViewProjectionMatrix" + i, lightViewProjectionsMatrices[i]); + //setting params to recieving geometry list + setMatParams(); + //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); } - renderManager.setForcedMaterial(postshadowMat); - + + //forcing the post shadow technique and render state + renderManager.setForcedTechnique(postTechniqueName); + renderManager.setForcedRenderState(state); + + //rendering the post shadow pass viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues); + //resetting renderManager settings + renderManager.setForcedTechnique(null); renderManager.setForcedMaterial(null); + renderManager.setForcedRenderState(null); renderManager.setCamera(cam, false); } @@ -447,6 +474,42 @@ public class PssmShadowRenderer implements SceneProcessor { } } + private void setMatParams() { + + GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive); + + //iteratin throught all the geometries of the list to set the material params + for (int i = 0; i < l.size(); i++) { + Material mat = l.get(i).getMaterial(); + //checking if the material has the post technique and setting the params. + if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) { + mat.setColor("Splits", splits); + postshadowMat.setColor("Splits", splits); + for (int j = 0; j < nbSplits; j++) { + mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[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); + } else { + needsfallBackMaterial = true; + } + } + + + //At least one material of the receiving geoms does not support the post shadow techniques + //so we fall back to the forced material solution (transparent shadows won't be supported for these objects) + if (needsfallBackMaterial) { + postshadowMat.setColor("Splits", splits); + for (int j = 0; j < nbSplits; j++) { + postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); + } + } + + } + public void preFrame(float tpf) { } @@ -511,7 +574,7 @@ public class PssmShadowRenderer implements SceneProcessor { * default is 0.7 * @param shadowIntensity the darkness of the shadow */ - public void setShadowIntensity(float shadowIntensity) { + final public void setShadowIntensity(float shadowIntensity) { this.shadowIntensity = shadowIntensity; postshadowMat.setFloat("ShadowIntensity", shadowIntensity); } diff --git a/engine/src/test/jme3test/light/TestTransparentShadow.java b/engine/src/test/jme3test/light/TestTransparentShadow.java index 727dfa8bc..47b672181 100644 --- a/engine/src/test/jme3test/light/TestTransparentShadow.java +++ b/engine/src/test/jme3test/light/TestTransparentShadow.java @@ -42,10 +42,9 @@ import com.jme3.math.*; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; -import com.jme3.scene.SceneGraphVisitorAdapter; import com.jme3.scene.Spatial; -import com.jme3.scene.control.LodControl; import com.jme3.scene.shape.Quad; +import com.jme3.scene.shape.Sphere; import com.jme3.shadow.PssmShadowRenderer; import com.jme3.shadow.PssmShadowRenderer.CompareMode; import com.jme3.shadow.PssmShadowRenderer.FilterMode; @@ -59,15 +58,16 @@ public class TestTransparentShadow extends SimpleApplication { public void simpleInitApp() { - cam.setLocation(new Vector3f(2.0606942f, 3.20342f, 6.7860126f)); - cam.setRotation(new Quaternion(-0.017481906f, 0.98241085f, -0.12393151f, -0.13857932f)); + cam.setLocation(new Vector3f(5.700248f, 6.161693f, 5.1404157f)); + cam.setRotation(new Quaternion(-0.09441641f, 0.8993388f, -0.24089815f, -0.35248178f)); viewPort.setBackgroundColor(ColorRGBA.DarkGray); Quad q = new Quad(20, 20); - q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(5)); + q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(10)); Geometry geom = new Geometry("floor", q); Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); + mat.setFloat("Shininess", 0); geom.setMaterial(mat); geom.rotate(-FastMath.HALF_PI, 0, 0); @@ -76,34 +76,21 @@ public class TestTransparentShadow extends SimpleApplication { rootNode.attachChild(geom); // create the geometry and attach it - Spatial teaGeom = assetManager.loadModel("Models/Tree/Tree.mesh.j3o"); - teaGeom.setQueueBucket(Bucket.Transparent); - teaGeom.setShadowMode(ShadowMode.Cast); + Spatial tree = assetManager.loadModel("Models/Tree/Tree.mesh.j3o"); + tree.setQueueBucket(Bucket.Transparent); + tree.setShadowMode(ShadowMode.CastAndReceive); - teaGeom.depthFirstTraversal(new SceneGraphVisitorAdapter(){ - @Override - public void visit(Geometry geom) { - LodControl lodCtrl = new LodControl(); - lodCtrl.setTrisPerPixel(0.25f); - geom.addControl(lodCtrl); - } - }); - + AmbientLight al = new AmbientLight(); - al.setColor(ColorRGBA.White.mult(2)); + al.setColor(ColorRGBA.White.mult(0.7f)); rootNode.addLight(al); DirectionalLight dl1 = new DirectionalLight(); - dl1.setDirection(new Vector3f(1, -1, 1).normalizeLocal()); - dl1.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f)); + dl1.setDirection(new Vector3f(0, -1, 0.5f).normalizeLocal()); + dl1.setColor(ColorRGBA.White.mult(1.5f)); rootNode.addLight(dl1); - DirectionalLight dl = new DirectionalLight(); - dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); - dl.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f)); - rootNode.addLight(dl); - - rootNode.attachChild(teaGeom); + rootNode.attachChild(tree); /** Uses Texture from jme3-test-data library! */ ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30); @@ -125,16 +112,26 @@ public class TestTransparentShadow extends SimpleApplication { fire.setLocalTranslation(1.0f, 0, 1.0f); fire.setLocalScale(0.3f); fire.setQueueBucket(Bucket.Translucent); - rootNode.attachChild(fire); + // rootNode.attachChild(fire); + Material mat2 = assetManager.loadMaterial("Common/Materials/RedColor.j3m"); + + + Geometry ball = new Geometry("sphere", new Sphere(16, 16, 0.5f)); + ball.setMaterial(mat2); + ball.setShadowMode(ShadowMode.CastAndReceive); + rootNode.attachChild(ball); + ball.setLocalTranslation(-1.0f, 1.5f, 1.0f); + + PssmShadowRenderer pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 1); - pssmRenderer.setDirection(new Vector3f(0.01f, -1f, 0.01f).normalizeLocal()); + pssmRenderer.setDirection(dl1.getDirection()); pssmRenderer.setLambda(0.55f); - pssmRenderer.setShadowIntensity(0.6f); + pssmRenderer.setShadowIntensity(0.8f); pssmRenderer.setCompareMode(CompareMode.Software); pssmRenderer.setFilterMode(FilterMode.PCF4); - pssmRenderer.displayDebug(); + //pssmRenderer.displayDebug(); viewPort.addProcessor(pssmRenderer); } }