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 ce7e35bc5..8b4e3faf8 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java +++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java @@ -122,6 +122,47 @@ public abstract class AbstractShadowFilter ext shadowRenderer.initialize(renderManager, vp); this.viewPort = vp; } + + /** + * 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(); + } /** * returns the shdaow intensity 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 6a9865da0..ae495b2b6 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java @@ -44,6 +44,7 @@ import com.jme3.export.Savable; 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; @@ -109,11 +110,17 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable protected GeometryList shadowMapOccluders = new GeometryList(new OpaqueComparator()); private String[] shadowMapStringCache; private String[] lightViewStringCache; + /** + * fade shadows at distance + */ + protected float zFarOverride = 0; + protected Vector2f fadeInfo; + protected float fadeLength; + protected Camera frustumCam; /** * true to skip the post pass when there are no shadow casters */ protected boolean skipPostPass; - /** * used for serialization @@ -319,7 +326,15 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } else { postTechniqueName = "PostShadow"; } + if(zFarOverride>0 && frustumCam == null){ + initFrustumCam(); + } } + + /** + * delegates the initialization of the frustum cam to child renderers + */ + protected abstract void initFrustumCam(); /** * Test whether this shadow renderer has been initialized. @@ -373,25 +388,25 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast); sceneReceivers = rq.getShadowQueueContent(ShadowMode.Receive); skipPostPass = false; - if (sceneReceivers.size() == 0 || occluders.size() == 0) { + if (sceneReceivers.size() == 0 || occluders.size() == 0 || !checkCulling(viewPort.getCamera())) { skipPostPass = true; 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, sceneReceivers); + if (debugfrustums) { + doDisplayFrustumDebug(shadowMapIndex); + } + renderShadowMap(shadowMapIndex, occluders, sceneReceivers); - } + } debugfrustums = false; if (flushQueues) { @@ -402,7 +417,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable renderManager.setForcedMaterial(null); renderManager.setForcedTechnique(null); renderManager.setCamera(viewPort.getCamera(), false); - + } protected void renderShadowMap(int shadowMapIndex, GeometryList occluders, GeometryList receivers) { @@ -512,6 +527,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable for (int j = 1; j < nbShadowMaps; j++) { mat.clearParam(shadowMapStringCache[j]); } + mat.clearParam("FadeInfo"); clearMaterialParameters(mat); } //No need to clear the postShadowMat params as the instance is locale to each renderer @@ -559,7 +575,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable mat.setInt("FilterMode", edgeFilteringMode.getMaterialParamValue()); mat.setFloat("PCFEdge", edgesThickness); mat.setFloat("ShadowIntensity", shadowIntensity); - + if (fadeInfo != null) { + mat.setVector2("FadeInfo", fadeInfo); + } setMaterialParameters(mat); } @@ -580,9 +598,86 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable postshadowMat.setMatrix4(lightViewStringCache[j], lightViewProjectionsMatrices[j]); postshadowMat.setTexture(shadowMapStringCache[j], shadowMaps[j]); } + if (fadeInfo != null) { + postshadowMat.setVector2("FadeInfo", fadeInfo); + } + } + + /** + * 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) { + this.zFarOverride = zFar; + if(zFarOverride == 0){ + fadeInfo = null; + frustumCam = null; + }else{ + if (fadeInfo != null) { + fadeInfo.set(zFarOverride - fadeLength, 1f / fadeLength); + } + if(frustumCam == null && viewPort != null){ + initFrustumCam(); + } + } + } + + /** + * 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; + } + + /** + * returns true if the light source bounding box is in the view frustum + * @return + */ + protected abstract boolean checkCulling(Camera viewCam); - public void preFrame(float tpf) { + public void preFrame(float tpf) { } public void cleanup() { diff --git a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowFilter.java b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowFilter.java index 21b032ec1..844755cb7 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowFilter.java +++ b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowFilter.java @@ -114,47 +114,6 @@ public class DirectionalLightShadowFilter extends AbstractShadowFilter 0)); + shadowZfarText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 120, 0); + guiNode.attachChild(shadowZfarText); } - private BitmapText shadowStabilizationText ; + private BitmapText shadowStabilizationText; + private BitmapText shadowZfarText; public void onAction(String name, boolean keyPressed, float tpf) { @@ -254,9 +263,25 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act if (name.equals("stabilize") && keyPressed) { dlsr.setEnabledStabilization(!dlsr.isEnabledStabilization()); - dlsf.setEnabledStabilization(!dlsf.isEnabledStabilization()); + dlsf.setEnabledStabilization(!dlsf.isEnabledStabilization()); shadowStabilizationText.setText("(b:on/off) Shadow stabilization : " + dlsr.isEnabledStabilization()); } + if (name.equals("distance") && keyPressed) { + if (dlsr.getShadowZExtend() > 0) { + dlsr.setShadowZExtend(0); + dlsr.setShadowZFadeLength(0); + dlsf.setShadowZExtend(0); + dlsf.setShadowZFadeLength(0); + + } else { + dlsr.setShadowZExtend(500); + dlsr.setShadowZFadeLength(50); + dlsf.setShadowZExtend(500); + dlsf.setShadowZFadeLength(50); + } + shadowZfarText.setText("(n:on/off) Shadow extend to 500 and fade to 50 : " + (dlsr.getShadowZExtend() > 0)); + + } if (name.equals("switchGroundMat") && keyPressed) { if (ground.getMaterial() == matGroundL) { diff --git a/jme3-examples/src/main/java/jme3test/light/TestPointLightShadows.java b/jme3-examples/src/main/java/jme3test/light/TestPointLightShadows.java index 114c3b6fb..a98ac03db 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestPointLightShadows.java +++ b/jme3-examples/src/main/java/jme3test/light/TestPointLightShadows.java @@ -87,33 +87,18 @@ public class TestPointLightShadows extends SimpleApplication { plsr = new PointLightShadowRenderer(assetManager, SHADOWMAP_SIZE); plsr.setLight((PointLight) scene.getLocalLightList().get(0)); plsr.setEdgeFilteringMode(EdgeFilteringMode.PCF4); + plsr.setShadowZExtend(15); + plsr.setShadowZFadeLength(5); // plsr.setFlushQueues(false); //plsr.displayFrustum(); 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.setShadowIntensity(0.3f); -// plsr2.setLight(pl); -// plsr2.setEdgeFilteringMode(EdgeFilteringMode.PCF4); -// // plsr.displayDebug(); -// viewPort.addProcessor(plsr2); - - plsf = new PointLightShadowFilter(assetManager, SHADOWMAP_SIZE); - plsf.setLight((PointLight) scene.getLocalLightList().get(0)); + plsf.setLight((PointLight) scene.getLocalLightList().get(0)); + plsf.setShadowZExtend(15); + plsf.setShadowZFadeLength(5); plsf.setEdgeFilteringMode(EdgeFilteringMode.PCF4); plsf.setEnabled(false); diff --git a/jme3-examples/src/main/java/jme3test/light/TestSpotLightShadows.java b/jme3-examples/src/main/java/jme3test/light/TestSpotLightShadows.java index 88f5db1c6..fbf0e1b67 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestSpotLightShadows.java +++ b/jme3-examples/src/main/java/jme3test/light/TestSpotLightShadows.java @@ -36,7 +36,6 @@ 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.*; @@ -46,12 +45,11 @@ 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.MaterialDebugAppState; import com.jme3.util.TangentBinormalGenerator; public class TestSpotLightShadows extends SimpleApplication { @@ -78,7 +76,7 @@ public class TestSpotLightShadows extends SimpleApplication { 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.setDirection(lightTarget.subtract(spot.getPosition()).normalizeLocal()); spot.setColor(ColorRGBA.White.mult(2)); rootNode.addLight(spot); @@ -103,12 +101,16 @@ public class TestSpotLightShadows extends SimpleApplication { final SpotLightShadowRenderer slsr = new SpotLightShadowRenderer(assetManager, 512); slsr.setLight(spot); slsr.setShadowIntensity(0.5f); + slsr.setShadowZExtend(100); + slsr.setShadowZFadeLength(5); slsr.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON); viewPort.addProcessor(slsr); SpotLightShadowFilter slsf = new SpotLightShadowFilter(assetManager, 512); slsf.setLight(spot); slsf.setShadowIntensity(0.5f); + slsf.setShadowZExtend(100); + slsf.setShadowZFadeLength(5); slsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON); slsf.setEnabled(false); @@ -130,7 +132,12 @@ public class TestSpotLightShadows extends SimpleApplication { }, "stop"); inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_1)); - + + MaterialDebugAppState s = new MaterialDebugAppState(); + s.registerBinding("Common/MatDefs/Shadow/PostShadow15.frag", rootNode); + s.registerBinding(new KeyTrigger(KeyInput.KEY_R), rootNode); + stateManager.attach(s); + flyCam.setDragToRotate(true); } public void setupFloor() {