Browse Source

Implemented stable shadows for DirectionalLightShadowRenderer

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10515 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
rem..om 12 years ago
parent
commit
b5014c5fbc

+ 3 - 2
engine/src/core/com/jme3/shadow/BasicShadowRenderer.java

@@ -69,6 +69,7 @@ public class BasicShadowRenderer implements SceneProcessor {
     private Vector3f[] points = new Vector3f[8];
     private Vector3f direction = new Vector3f();
     protected Texture2D dummyTex;
+    private float shadowMapSize;
 
     /**
      * Creates a BasicShadowRenderer
@@ -84,7 +85,7 @@ public class BasicShadowRenderer implements SceneProcessor {
          //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
         dummyTex = new Texture2D(size, size, Format.RGBA8);        
         shadowFB.setColorTexture(dummyTex);
-
+        shadowMapSize = (float)size;
         preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
         postshadowMat = new Material(manager, "Common/MatDefs/Shadow/BasicPostShadow.j3md");
         postshadowMat.setTexture("ShadowMap", shadowMap);
@@ -177,7 +178,7 @@ public class BasicShadowRenderer implements SceneProcessor {
         shadowCam.updateViewProjection();
 
         // render shadow casters to shadow map
-        ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points);
+        ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points, shadowMapSize);
 
         Renderer r = renderManager.getRenderer();
         renderManager.setCamera(shadowCam, false);

+ 18 - 1
engine/src/core/com/jme3/shadow/DirectionalLightShadowFilter.java

@@ -37,7 +37,6 @@ import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
 import com.jme3.light.DirectionalLight;
-import com.jme3.material.Material;
 import java.io.IOException;
 
 /**
@@ -155,6 +154,24 @@ public class DirectionalLightShadowFilter extends AbstractShadowFilter<Direction
     public float getShadowZFadeLength() {
         return shadowRenderer.getShadowZFadeLength();
     }
+    
+    /**
+     * retruns true if stabilization is enabled
+     * @return 
+     */
+    public boolean isEnabledStabilization() {
+        return shadowRenderer.isEnabledStabilization();
+    }
+    
+    /**
+     * Enables the stabilization of the shadows's edges. (default is true)
+     * This prevents shadows' edges to flicker when the camera moves
+     * However it can lead to some shadow quality loss in some particular scenes.
+     * @param stabilize 
+     */
+    public void setEnabledStabilization(boolean stabilize) {
+        shadowRenderer.setEnabledStabilization(stabilize);        
+    }    
 
     @Override
     public void write(JmeExporter ex) throws IOException {

+ 22 - 3
engine/src/core/com/jme3/shadow/DirectionalLightShadowRenderer.java

@@ -69,6 +69,7 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {
     //Holding the info for fading shadows in the far distance 
     protected Vector2f fadeInfo;
     protected float fadeLength;
+    private boolean stabilize = true;
 
     /**
      * Used for serialzation use
@@ -166,15 +167,15 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {
         }
 
     }
-
+    
     @Override
     protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers, GeometryList shadowMapOccluders) {
 
         // update frustum points based on current camera and split
         ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], 1.0f, points);
 
-        //Updating shadow cam with curent split frustra
-        ShadowUtil.updateShadowCamera(sceneOccluders, sceneReceivers, shadowCam, points, shadowMapOccluders);
+        //Updating shadow cam with curent split frustra        
+        ShadowUtil.updateShadowCamera(sceneOccluders, sceneReceivers, shadowCam, points, shadowMapOccluders, stabilize?shadowMapSize:0);
 
         return shadowMapOccluders;
     }
@@ -283,6 +284,24 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {
         return 0f;
     }
 
+    /**
+     * retruns true if stabilization is enabled
+     * @return 
+     */
+    public boolean isEnabledStabilization() {
+        return stabilize;
+    }
+    
+    /**
+     * Enables the stabilization of the shadows's edges. (default is true)
+     * This prevents shadows' edges to flicker when the camera moves
+     * However it can lead to some shadow quality loss in some particular scenes.
+     * @param stabilize 
+     */
+    public void setEnabledStabilization(boolean stabilize) {
+        this.stabilize = stabilize;
+    }
+    
     @Override
     public void read(JmeImporter im) throws IOException {
         super.read(im);

+ 1 - 1
engine/src/core/com/jme3/shadow/PssmShadowRenderer.java

@@ -437,7 +437,7 @@ public class PssmShadowRenderer implements SceneProcessor {
             ShadowUtil.updateFrustumPoints(viewCam, splitsArray[i], splitsArray[i + 1], 1.0f, points);
 
             //Updating shadow cam with curent split frustra
-            ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points, splitOccluders);
+            ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points, splitOccluders, shadowMapSize);
 
             //saving light view projection matrix for this split            
             lightViewProjectionsMatrices[i].set(shadowCam.getViewProjectionMatrix());

+ 82 - 61
engine/src/core/com/jme3/shadow/ShadowUtil.java

@@ -33,6 +33,7 @@ package com.jme3.shadow;
 
 import com.jme3.bounding.BoundingBox;
 import com.jme3.bounding.BoundingVolume;
+import com.jme3.math.FastMath;
 import com.jme3.math.Matrix4f;
 import com.jme3.math.Transform;
 import com.jme3.math.Vector2f;
@@ -44,7 +45,6 @@ import com.jme3.util.TempVars;
 import static java.lang.Math.max;
 import static java.lang.Math.min;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -67,12 +67,12 @@ public class ShadowUtil {
     public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) {
         int w = viewCam.getWidth();
         int h = viewCam.getHeight();
-        
+
         points[0].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 0));
         points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 0));
         points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 0));
         points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), 0));
-        
+
         points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 1));
         points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 1));
         points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 1));
@@ -95,23 +95,23 @@ public class ShadowUtil {
             float farOverride,
             float scale,
             Vector3f[] points) {
-        
+
         Vector3f pos = viewCam.getLocation();
         Vector3f dir = viewCam.getDirection();
         Vector3f up = viewCam.getUp();
-        
+
         float depthHeightRatio = viewCam.getFrustumTop() / viewCam.getFrustumNear();
         float near = nearOverride;
         float far = farOverride;
         float ftop = viewCam.getFrustumTop();
         float fright = viewCam.getFrustumRight();
         float ratio = fright / ftop;
-        
+
         float near_height;
         float near_width;
         float far_height;
         float far_width;
-        
+
         if (viewCam.isParallelProjection()) {
             near_height = ftop;
             near_width = near_height * ratio;
@@ -123,30 +123,30 @@ public class ShadowUtil {
             far_height = depthHeightRatio * far;
             far_width = far_height * ratio;
         }
-        
+
         Vector3f right = dir.cross(up).normalizeLocal();
-        
+
         Vector3f temp = new Vector3f();
         temp.set(dir).multLocal(far).addLocal(pos);
         Vector3f farCenter = temp.clone();
         temp.set(dir).multLocal(near).addLocal(pos);
         Vector3f nearCenter = temp.clone();
-        
+
         Vector3f nearUp = temp.set(up).multLocal(near_height).clone();
         Vector3f farUp = temp.set(up).multLocal(far_height).clone();
         Vector3f nearRight = temp.set(right).multLocal(near_width).clone();
         Vector3f farRight = temp.set(right).multLocal(far_width).clone();
-        
+
         points[0].set(nearCenter).subtractLocal(nearUp).subtractLocal(nearRight);
         points[1].set(nearCenter).addLocal(nearUp).subtractLocal(nearRight);
         points[2].set(nearCenter).addLocal(nearUp).addLocal(nearRight);
         points[3].set(nearCenter).subtractLocal(nearUp).addLocal(nearRight);
-        
+
         points[4].set(farCenter).subtractLocal(farUp).subtractLocal(farRight);
         points[5].set(farCenter).addLocal(farUp).subtractLocal(farRight);
         points[6].set(farCenter).addLocal(farUp).addLocal(farRight);
         points[7].set(farCenter).subtractLocal(farUp).addLocal(farRight);
-        
+
         if (scale != 1.0f) {
             // find center of frustum
             Vector3f center = new Vector3f();
@@ -154,7 +154,7 @@ public class ShadowUtil {
                 center.addLocal(points[i]);
             }
             center.divideLocal(8f);
-            
+
             Vector3f cDir = new Vector3f();
             for (int i = 0; i < 8; i++) {
                 cDir.set(points[i]).subtractLocal(center);
@@ -231,7 +231,7 @@ public class ShadowUtil {
         Vector3f temp = new Vector3f();
         for (int i = 0; i < pts.length; i++) {
             transform.transformVector(pts[i], temp);
-            
+
             min.minLocal(temp);
             max.maxLocal(temp);
         }
@@ -251,15 +251,15 @@ public class ShadowUtil {
         Vector3f max = new Vector3f(Vector3f.NEGATIVE_INFINITY);
         TempVars vars = TempVars.get();
         Vector3f temp = vars.vect1;
-        
+
         for (int i = 0; i < pts.length; i++) {
             float w = mat.multProj(pts[i], temp);
-            
+
             temp.x /= w;
             temp.y /= w;
             // Why was this commented out?
             temp.z /= w;
-            
+
             min.minLocal(temp);
             max.maxLocal(temp);
         }
@@ -280,20 +280,20 @@ public class ShadowUtil {
     public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) {
         boolean ortho = shadowCam.isParallelProjection();
         shadowCam.setProjectionMatrix(null);
-        
+
         if (ortho) {
             shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
         } else {
             shadowCam.setFrustumPerspective(45, 1, 1, 150);
         }
-        
+
         Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();
         Matrix4f projMatrix = shadowCam.getProjectionMatrix();
-        
+
         BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);
-        
+
         TempVars vars = TempVars.get();
-        
+
         Vector3f splitMin = splitBB.getMin(vars.vect1);
         Vector3f splitMax = splitBB.getMax(vars.vect2);
 
@@ -302,25 +302,25 @@ public class ShadowUtil {
         // Create the crop matrix.
         float scaleX, scaleY, scaleZ;
         float offsetX, offsetY, offsetZ;
-        
+
         scaleX = 2.0f / (splitMax.x - splitMin.x);
         scaleY = 2.0f / (splitMax.y - splitMin.y);
         offsetX = -0.5f * (splitMax.x + splitMin.x) * scaleX;
         offsetY = -0.5f * (splitMax.y + splitMin.y) * scaleY;
         scaleZ = 1.0f / (splitMax.z - splitMin.z);
         offsetZ = -splitMin.z * scaleZ;
-        
+
         Matrix4f cropMatrix = vars.tempMat4;
         cropMatrix.set(scaleX, 0f, 0f, offsetX,
                 0f, scaleY, 0f, offsetY,
                 0f, 0f, scaleZ, offsetZ,
                 0f, 0f, 0f, 1f);
-        
-        
+
+
         Matrix4f result = new Matrix4f();
         result.set(cropMatrix);
         result.multLocal(projMatrix);
-        
+
         vars.release();
         shadowCam.setProjectionMatrix(result);
     }
@@ -337,8 +337,9 @@ public class ShadowUtil {
     public static void updateShadowCamera(GeometryList occluders,
             GeometryList receivers,
             Camera shadowCam,
-            Vector3f[] points) {
-        updateShadowCamera(occluders, receivers, shadowCam, points, null);
+            Vector3f[] points,
+            float shadowMapSize) {
+        updateShadowCamera(occluders, receivers, shadowCam, points, null, shadowMapSize);
     }
 
     /**
@@ -353,40 +354,41 @@ public class ShadowUtil {
             GeometryList receivers,
             Camera shadowCam,
             Vector3f[] points,
-            GeometryList splitOccluders) {
-        
+            GeometryList splitOccluders,
+            float shadowMapSize) {
+
         boolean ortho = shadowCam.isParallelProjection();
-        
+
         shadowCam.setProjectionMatrix(null);
-        
+
         if (ortho) {
             shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
         }
 
         // create transform to rotate points to viewspace        
         Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();
-        
+
         BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);
-        
+
         ArrayList<BoundingVolume> visRecvList = new ArrayList<BoundingVolume>();
         for (int i = 0; i < receivers.size(); i++) {
             // convert bounding box to light's viewproj space
             Geometry receiver = receivers.get(i);
             BoundingVolume bv = receiver.getWorldBound();
             BoundingVolume recvBox = bv.transform(viewProjMatrix, null);
-            
+
             if (splitBB.intersects(recvBox)) {
                 visRecvList.add(recvBox);
             }
         }
-        
+
         ArrayList<BoundingVolume> visOccList = new ArrayList<BoundingVolume>();
         for (int i = 0; i < occluders.size(); i++) {
             // convert bounding box to light's viewproj space
             Geometry occluder = occluders.get(i);
             BoundingVolume bv = occluder.getWorldBound();
             BoundingVolume occBox = bv.transform(viewProjMatrix, null);
-            
+
             boolean intersects = splitBB.intersects(occBox);
             if (!intersects && occBox instanceof BoundingBox) {
                 BoundingBox occBB = (BoundingBox) occBox;
@@ -416,7 +418,7 @@ public class ShadowUtil {
                 }
             }
         }
-        
+
         BoundingBox casterBB = computeUnionBound(visOccList);
         BoundingBox receiverBB = computeUnionBound(visRecvList);
 
@@ -426,18 +428,18 @@ public class ShadowUtil {
             casterBB.setYExtent(casterBB.getYExtent() + 2.0f);
             casterBB.setZExtent(casterBB.getZExtent() + 2.0f);
         }
-        
+
         TempVars vars = TempVars.get();
-        
+
         Vector3f casterMin = casterBB.getMin(vars.vect1);
         Vector3f casterMax = casterBB.getMax(vars.vect2);
-        
+
         Vector3f receiverMin = receiverBB.getMin(vars.vect3);
         Vector3f receiverMax = receiverBB.getMax(vars.vect4);
-        
+
         Vector3f splitMin = splitBB.getMin(vars.vect5);
         Vector3f splitMax = splitBB.getMax(vars.vect6);
-        
+
         splitMin.z = 0;
 
 //        if (!ortho) {
@@ -445,17 +447,17 @@ public class ShadowUtil {
 //        }
 
         Matrix4f projMatrix = shadowCam.getProjectionMatrix();
-        
+
         Vector3f cropMin = vars.vect7;
         Vector3f cropMax = vars.vect8;
 
         // IMPORTANT: Special handling for Z values
         cropMin.x = max(max(casterMin.x, receiverMin.x), splitMin.x);
         cropMax.x = min(min(casterMax.x, receiverMax.x), splitMax.x);
-        
+
         cropMin.y = max(max(casterMin.y, receiverMin.y), splitMin.y);
         cropMax.y = min(min(casterMax.y, receiverMax.y), splitMax.y);
-        
+
         cropMin.z = min(casterMin.z, splitMin.z);
         cropMax.z = min(receiverMax.z, splitMax.z);
 
@@ -463,33 +465,52 @@ public class ShadowUtil {
         // Create the crop matrix.
         float scaleX, scaleY, scaleZ;
         float offsetX, offsetY, offsetZ;
-        
+
         scaleX = (2.0f) / (cropMax.x - cropMin.x);
         scaleY = (2.0f) / (cropMax.y - cropMin.y);
-        
+
+        //Shadow map stabilization approximation from shaderX 7
+        //from Practical Cascaded Shadow maps adapted to PSSM
+        //scale stabilization
+        float halfTextureSize = shadowMapSize * 0.5f;
+        if (halfTextureSize != 0) {
+            float scaleQuantizer = 0.1f;            
+            scaleX = 1.0f / FastMath.ceil(1.0f / scaleX * scaleQuantizer) * scaleQuantizer;
+            scaleY = 1.0f / FastMath.ceil(1.0f / scaleY * scaleQuantizer) * scaleQuantizer;
+        }
+
         offsetX = -0.5f * (cropMax.x + cropMin.x) * scaleX;
         offsetY = -0.5f * (cropMax.y + cropMin.y) * scaleY;
-        
+
+
+        //Shadow map stabilization approximation from shaderX 7
+        //from Practical Cascaded Shadow maps adapted to PSSM
+        //offset stabilization
+        if (halfTextureSize != 0) {
+            offsetX = FastMath.ceil(offsetX * halfTextureSize) / halfTextureSize;
+            offsetY = FastMath.ceil(offsetY * halfTextureSize) / halfTextureSize;
+        }
+
         scaleZ = 1.0f / (cropMax.z - cropMin.z);
         offsetZ = -cropMin.z * scaleZ;
-        
-        
-        
-        
+
+
+
+
         Matrix4f cropMatrix = vars.tempMat4;
         cropMatrix.set(scaleX, 0f, 0f, offsetX,
                 0f, scaleY, 0f, offsetY,
                 0f, 0f, scaleZ, offsetZ,
                 0f, 0f, 0f, 1f);
-        
-        
+
+
         Matrix4f result = new Matrix4f();
         result.set(cropMatrix);
         result.multLocal(projMatrix);
         vars.release();
-        
+
         shadowCam.setProjectionMatrix(result);
-        
+
     }
 
     /**
@@ -514,7 +535,7 @@ public class ShadowUtil {
             }
             camera.setPlaneState(planeState);
         }
-        
+
     }
 
     /**
@@ -545,6 +566,6 @@ public class ShadowUtil {
                 outputGeometryList.add(g);
             }
         }
-        
+
     }
 }

+ 23 - 8
engine/src/test/jme3test/light/TestDirectionalLightShadow.java

@@ -32,6 +32,7 @@
 package jme3test.light;
 
 import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
 import com.jme3.input.KeyInput;
 import com.jme3.input.controls.ActionListener;
 import com.jme3.input.controls.AnalogListener;
@@ -59,6 +60,7 @@ import com.jme3.util.SkyFactory;
 import com.jme3.util.TangentBinormalGenerator;
 
 public class TestDirectionalLightShadow extends SimpleApplication implements ActionListener, AnalogListener {
+    public static final int SHADOWMAP_SIZE = 1024;
 
     private Spatial[] obj;
     private Material[] mat;
@@ -135,7 +137,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
         rootNode.attachChild(ground);
 
         l = new DirectionalLight();
-        //   l.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal());
+        //l.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal());
         l.setDirection(new Vector3f(-1, -1, -1));
         rootNode.addLight(l);
 
@@ -161,7 +163,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
 
         loadScene();
 
-        dlsr = new DirectionalLightShadowRenderer(assetManager, 1024, 3);
+        dlsr = new DirectionalLightShadowRenderer(assetManager, SHADOWMAP_SIZE, 3);
         dlsr.setLight(l);
         dlsr.setLambda(0.55f);
         dlsr.setShadowIntensity(0.6f);
@@ -169,7 +171,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
         dlsr.displayDebug();
         viewPort.addProcessor(dlsr);
 
-        dlsf = new DirectionalLightShadowFilter(assetManager, 1024, 3);
+        dlsf = new DirectionalLightShadowFilter(assetManager, SHADOWMAP_SIZE, 3);
         dlsf.setLight(l);
         dlsf.setLambda(0.55f);
         dlsf.setShadowIntensity(0.6f);
@@ -192,6 +194,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
         inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J));
         inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M));
         inputManager.addMapping("debug", new KeyTrigger(KeyInput.KEY_X));
+        inputManager.addMapping("stabilize", new KeyTrigger(KeyInput.KEY_B));
 
 
         inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_NUMPAD8));
@@ -204,14 +207,21 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
 
 
         inputManager.addListener(this, "lambdaUp", "lambdaDown", "ThicknessUp", "ThicknessDown",
-                "switchGroundMat", "debug", "up", "down", "right", "left", "fwd", "back","pp");
+                "switchGroundMat", "debug", "up", "down", "right", "left", "fwd", "back", "pp","stabilize");
 
         ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, dlsr, dlsf, guiNode, inputManager, viewPort);
-        
+
         inputManager.addListener(this, "Size+", "Size-");
-                inputManager.addMapping("Size+", new KeyTrigger(KeyInput.KEY_W));
-                inputManager.addMapping("Size-", new KeyTrigger(KeyInput.KEY_S));
+        inputManager.addMapping("Size+", new KeyTrigger(KeyInput.KEY_W));
+        inputManager.addMapping("Size-", new KeyTrigger(KeyInput.KEY_S));
+ 
+        shadowStabilizationText = new BitmapText(guiFont, false);
+        shadowStabilizationText.setSize(guiFont.getCharSet().getRenderedSize() * 0.75f);
+        shadowStabilizationText.setText("(b:on/off) Shadow stabilization : " + dlsr.isEnabledStabilization());
+        shadowStabilizationText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 100, 0);
+        guiNode.attachChild(shadowStabilizationText);
     }
+    private  BitmapText shadowStabilizationText ;
 
     public void onAction(String name, boolean keyPressed, float tpf) {
 
@@ -223,7 +233,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
                 cam.setParallelProjection(true);
                 float aspect = (float) cam.getWidth() / cam.getHeight();
                 cam.setFrustum(-1000, 1000, -aspect * frustumSize, aspect * frustumSize, frustumSize, -frustumSize);
-                
+
             }
         }
 
@@ -242,6 +252,11 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
             dlsr.displayFrustum();
         }
 
+        if (name.equals("stabilize") && keyPressed) {
+            dlsr.setEnabledStabilization(!dlsr.isEnabledStabilization());
+            dlsf.setEnabledStabilization(!dlsf.isEnabledStabilization());  
+            shadowStabilizationText.setText("(b:on/off) Shadow stabilization : " + dlsr.isEnabledStabilization());
+        }
 
         if (name.equals("switchGroundMat") && keyPressed) {
             if (ground.getMaterial() == matGroundL) {

+ 14 - 12
engine/src/test/jme3test/light/TestPointLightShadows.java

@@ -46,6 +46,7 @@ import com.jme3.shadow.PointLightShadowFilter;
 import com.jme3.shadow.PointLightShadowRenderer;
 
 public class TestPointLightShadows extends SimpleApplication {
+    public static final int SHADOWMAP_SIZE = 512;
 
     public static void main(String[] args) {
         TestPointLightShadows app = new TestPointLightShadows();
@@ -83,12 +84,12 @@ public class TestPointLightShadows extends SimpleApplication {
         box.setLocalTranslation(-1f, 0.5f, -2);
 
 
-
-        plsr = new PointLightShadowRenderer(assetManager, 512);
+        plsr = new PointLightShadowRenderer(assetManager, SHADOWMAP_SIZE);
         plsr.setLight((PointLight) scene.getLocalLightList().get(0));
-        plsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
-     //   plsr.setFlushQueues(false);
-        //   plsr.displayDebug();
+        plsr.setEdgeFilteringMode(EdgeFilteringMode.PCF4);
+       // plsr.setFlushQueues(false);
+        //plsr.displayFrustum();
+        plsr.displayDebug();
         viewPort.addProcessor(plsr);
 
 
@@ -96,7 +97,7 @@ public class TestPointLightShadows extends SimpleApplication {
 //        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"));
@@ -104,26 +105,27 @@ public class TestPointLightShadows extends SimpleApplication {
 //        rootNode.attachChild(lightMdl2);
 //        lightMdl2.setLocalTranslation(pl.getPosition());
 //        PointLightShadowRenderer plsr2 = new PointLightShadowRenderer(assetManager, 512);
+//        plsr2.setShadowIntensity(0.3f);
 //        plsr2.setLight(pl);
-//        plsr2.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+//        plsr2.setEdgeFilteringMode(EdgeFilteringMode.PCF4);
 //        //   plsr.displayDebug();
 //        viewPort.addProcessor(plsr2);
 
 
-        plsf = new PointLightShadowFilter(assetManager, 512);
-        plsf.setLight((PointLight) scene.getLocalLightList().get(0));
-        plsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+        plsf = new PointLightShadowFilter(assetManager, SHADOWMAP_SIZE);
+        plsf.setLight((PointLight) scene.getLocalLightList().get(0));     
+        plsf.setEdgeFilteringMode(EdgeFilteringMode.PCF4);
         plsf.setEnabled(false);
 
         FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
         fpp.addFilter(plsf);
         viewPort.addProcessor(fpp);
-
+              
         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);
+ //      lightNode.move(FastMath.cos(tpf) * 0.4f, 0, FastMath.sin(tpf) * 0.4f);
     }
 }

+ 41 - 27
engine/src/test/jme3test/light/TestTransparentShadow.java

@@ -29,12 +29,14 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package jme3test.light;
 
 import com.jme3.app.SimpleApplication;
 import com.jme3.effect.ParticleEmitter;
 import com.jme3.effect.ParticleMesh;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
 import com.jme3.light.AmbientLight;
 import com.jme3.light.DirectionalLight;
 import com.jme3.material.Material;
@@ -45,9 +47,9 @@ import com.jme3.scene.Geometry;
 import com.jme3.scene.Spatial;
 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;
+import com.jme3.shadow.CompareMode;
+import com.jme3.shadow.DirectionalLightShadowRenderer;
+import com.jme3.shadow.EdgeFilteringMode;
 
 public class TestTransparentShadow extends SimpleApplication {
 
@@ -69,7 +71,7 @@ public class TestTransparentShadow extends SimpleApplication {
         Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
         mat.setFloat("Shininess", 0);
         geom.setMaterial(mat);
-        
+
         geom.rotate(-FastMath.HALF_PI, 0, 0);
         geom.center();
         geom.setShadowMode(ShadowMode.CastAndReceive);
@@ -80,7 +82,7 @@ public class TestTransparentShadow extends SimpleApplication {
         tree.setQueueBucket(Bucket.Transparent);
         tree.setShadowMode(ShadowMode.CastAndReceive);
 
-           
+
         AmbientLight al = new AmbientLight();
         al.setColor(ColorRGBA.White.mult(0.7f));
         rootNode.addLight(al);
@@ -90,8 +92,8 @@ public class TestTransparentShadow extends SimpleApplication {
         dl1.setColor(ColorRGBA.White.mult(1.5f));
         rootNode.addLight(dl1);
 
-        rootNode.attachChild(tree);    
-        
+        rootNode.attachChild(tree);
+
         /** Uses Texture from jme3-test-data library! */
         ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
         Material mat_red = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
@@ -99,39 +101,51 @@ public class TestTransparentShadow extends SimpleApplication {
         //mat_red.getAdditionalRenderState().setDepthTest(true);
         //mat_red.getAdditionalRenderState().setDepthWrite(true);
         fire.setMaterial(mat_red);
-        fire.setImagesX(2); fire.setImagesY(2); // 2x2 texture animation
-        fire.setEndColor(  new ColorRGBA(1f, 0f, 0f, 1f));   // red
+        fire.setImagesX(2);
+        fire.setImagesY(2); // 2x2 texture animation
+        fire.setEndColor(new ColorRGBA(1f, 0f, 0f, 1f));   // red
         fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
-        fire.setInitialVelocity(new Vector3f(0, 2, 0));
+        fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 2, 0));
         fire.setStartSize(0.6f);
         fire.setEndSize(0.1f);
         fire.setGravity(0, 0, 0);
         fire.setLowLife(0.5f);
         fire.setHighLife(1.5f);
-        fire.setVelocityVariation(0.3f);
+        fire.getParticleInfluencer().setVelocityVariation(0.3f);
         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");
 
-        
-        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(dl1.getDirection());
-        pssmRenderer.setLambda(0.55f);
-        pssmRenderer.setShadowIntensity(0.8f);
-        pssmRenderer.setCompareMode(CompareMode.Software);
-        pssmRenderer.setFilterMode(FilterMode.PCF4);
-        //pssmRenderer.displayDebug();
-         viewPort.addProcessor(pssmRenderer);
-    }
+
+
+        final DirectionalLightShadowRenderer dlsRenderer = new DirectionalLightShadowRenderer(assetManager, 1024, 1);
+        dlsRenderer.setLight(dl1);
+        dlsRenderer.setLambda(0.55f);
+        dlsRenderer.setShadowIntensity(0.8f);
+        dlsRenderer.setShadowCompareMode(CompareMode.Software);
+        dlsRenderer.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+        dlsRenderer.displayDebug();
+        viewPort.addProcessor(dlsRenderer);
+        inputManager.addMapping("stabilize", new KeyTrigger(KeyInput.KEY_B));
+
+        inputManager.addListener(new ActionListener() {
+            @Override
+            public void onAction(String name, boolean isPressed, float tpf) {
+                if (name.equals("stabilize") && isPressed) {
+                    dlsRenderer.setEnabledStabilization(!dlsRenderer.isEnabledStabilization()) ;
+                }
+            }
+        }, "stabilize");
+
+   }
 }