DirectionalLight shadows

I’ve started using DirectionalLightShadowRenderer, and I’ve got a number of bugs. For instance, when I remove an object from the scene, its shadow persists. This is demonstrated by the test app below. Press the ‘N’ key to toggle the logo box in and out of the scene. Surely I must be doing something wrong, but what is it?

[java]
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.shadow.EdgeFilteringMode;
import com.jme3.texture.Texture;
/**

  • A simple JME3 app to demonstrate shadow rendering issues.
    */
    public class Main
    extends SimpleApplication
    implements ActionListener {

    static final int shadowMapSize = 1024;
    static final int nbSplits = 3;
    static final float lambda = 0.55f;
    static final float intensity = 0.6f;
    Geometry logoBox;

    public static void main(String[] args) {
    Main app = new Main();
    app.setShowSettings(false);
    app.start();
    }

    @Override
    public void simpleInitApp() {
    Vector3f startLocation = new Vector3f(-5f, 6f, -2f);
    cam.setLocation(startLocation);
    Quaternion startRotation = new Quaternion(0.2f, 0.7f, -0.3f, 0.6f);
    cam.setRotation(startRotation);
    flyCam.setMoveSpeed(10f);

     viewPort.setBackgroundColor(ColorRGBA.Gray);
    
     DirectionalLight mainLight = new DirectionalLight();
     mainLight.setColor(ColorRGBA.White);
     Vector3f lightDirection = new Vector3f(2f, -9f, -2f).normalize();
     mainLight.setDirection(lightDirection);
     mainLight.setName("main");
     rootNode.addLight(mainLight);
     /*
      * two geometries: yellow ground and logo box
      */
     Material yellow = new Material(assetManager,
             "Common/MatDefs/Light/Lighting.j3md");
     yellow.setBoolean("UseMaterialColors", true);
     yellow.setColor("Diffuse", ColorRGBA.Yellow);
    
     Box groundMesh = new Box(8f, 0.2f, 8f);
     Geometry ground = new Geometry("ground", groundMesh);
     ground.setMaterial(yellow);
     ground.setLocalTranslation(0f, -0.2f, 0f);
     ground.setShadowMode(RenderQueue.ShadowMode.Receive);
     rootNode.attachChild(ground);
    
     Material logoMaterial = new Material(assetManager,
             "Common/MatDefs/Light/Lighting.j3md");
     Texture logo = assetManager.loadTexture("Interface/Logo/Monkey.png");
     logoMaterial.setTexture("DiffuseMap", logo);
    
     Box boxMesh = new Box(0.3f, 0.3f, 0.3f);
     logoBox = new Geometry("box", boxMesh);
     logoBox.setLocalTranslation(0f, 0.6f, -3f);
     logoBox.setMaterial(logoMaterial);
     logoBox.setShadowMode(RenderQueue.ShadowMode.Cast);
     rootNode.attachChild(logoBox);
     /*
      * Add shadow processors.
      */
     DirectionalLightShadowRenderer dlsr =
             new DirectionalLightShadowRenderer(assetManager, shadowMapSize,
             nbSplits);
     dlsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
     dlsr.setLambda(lambda);
     dlsr.setLight(mainLight);
     dlsr.setShadowIntensity(intensity);
     viewPort.addProcessor(dlsr);
    
     DirectionalLightShadowFilter filter =
             new DirectionalLightShadowFilter(assetManager, shadowMapSize,
             nbSplits);
     filter.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
     filter.setEnabled(true);
     filter.setLambda(lambda);
     filter.setLight(mainLight);
     filter.setShadowIntensity(intensity);
    
     FilterPostProcessor postProcessor =
             new FilterPostProcessor(assetManager);
     postProcessor.addFilter(filter);
     viewPort.addProcessor(postProcessor);
     /*
      * Press 'N' key to toggle the logo box in or out of the scene.
      */
     inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_N));
     inputManager.addListener(this, "toggle");
    

    }

    @Override
    public void onAction(String actionString, boolean ongoing, float unused) {
    if (!ongoing) {
    return;
    }
    if (“toggle”.equals(actionString)) {
    if (logoBox.getParent() == null) {
    System.out.println("\n ATTACHING logo box\n");
    rootNode.attachChild(logoBox);

         } else {
             System.out.println("\n DETACHING logo box\n");
             rootNode.detachChild(logoBox);
         }
     }
    

    }
    }
    [/java]

you have both the shadowRenderer and the ShadowFilter. This can’t get something right.
You have to choose one or the other.
In the test case, I use both but they are never activated at the same time.

ShadowRenderer is the classic renderer that render the post pass as an additional geometry pass (meaning that the whole view is rendered once more). This is acceptable for not much crowded scenes and you can exclude some objects from the shadows by using the setShadowMode method on them.

ShadowFilter renders the post pass in a post process style, meaning in 2D on a full screen quad. This is a lot faster for scenes that have a fair amount of objects (in my tests it was around 30). The draw back is that all geometries that write depth (that is everything if you don’t specify otherwise) recieve shadows, and the shadowMode can’t help. ShadowMode still works to know what cast shadows though.

here is more in depth post where I exposed some test results about this
http://hub.jmonkeyengine.org/forum/topic/silly-question-about-shadow-rendering/

1 Like

Thanks, @Nehon, that’s good to know. But if I comment out one method or the other–take your pick–I still see shadows which persist after the casting geometry is removed from the scene.

So something else must be wrong here.

I’ll look into it

2 Likes

Ok this happens because the logobox geometry is the only one casting shadows. When you remove it there is no shadow caster anymore so the pre shadow pass is bypassed…but not the post shadow one and it keeps the last shadow map rendered.
Someone already reported a similar issue. I’ll see if I can fix that.

Note that this does not occur since you still have objects casting shadows in the scene (in your test case if you add a second box it works), so you can probably use this as a workaround for now.

1 Like

It should be fixed now in last SVN

2 Likes

Thank you very much, @Nehon!

Will the fix be back-ported into the 3.0final branch?

Well I’d rather not to be honest.I’d like only blocking issues to be back ported to 3.0 otherwise it’s gonna be endless and we’ll have a 3.0.378 before 3.1

Also some things have changed with this fix and I’d rather it to be battle tested anyway before we think about back porting it.

I understand. I just feel compelled to ask.

I do seem to have a knack for tripping over obscure bugs that affect me and nobody else. Thanks again for all your help in getting them resolved quickly!

This old “bug” has been fixed :slight_smile: sgold, you aren’t the only one encountering this one but I think no one has been concerned about it. I think no one has had a “real” scene in a game where all shadow casters are removed. There are usually some trees or buildings or something left that casts shadows and then this hasn’t been a problem.

Each obscure or undocumented software defect is a potential pitfall for new developers and makes the learning curve a little steeper than it needs to be.