Directional Light shadow filter doesn't work with instanced geometry

Hello guys. I can’t activate shadow casting for instanced objects. Here is sample code based on official example.

import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
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.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.instancing.InstancedNode;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.system.AppSettings;

public class TestInstancedNode extends SimpleApplication  {
    private Mesh mesh1;
    private Mesh mesh2;
    private final Material[] materials = new Material[2];
    private Node instancedNode;
    private boolean INSTANCING = false;
    
    public static void main(String[] args){
        TestInstancedNode app = new TestInstancedNode();
        AppSettings settings = new AppSettings(true);
        settings.setVSync(false);
        app.setSettings(settings);
        app.start();
    }

    private Geometry createInstance(float x, float z) {
        Mesh mesh; 
        if (FastMath.nextRandomInt(0, 1) == 1) mesh = mesh2;
        else mesh = mesh1;
        Geometry geometry = new Geometry("randomGeom", mesh);
        geometry.setMaterial(materials[FastMath.nextRandomInt(0, materials.length - 1)]);
        geometry.setLocalTranslation(x, x + z, z);
        return geometry;
    }
    
    @Override
    public void simpleInitApp() {
        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        
        DirectionalLight directionalLight = new DirectionalLight(new Vector3f(0.9f, 0.1f, 0.8f), ColorRGBA.Pink);
        AmbientLight ambientLight = new AmbientLight(ColorRGBA.Gray);
        rootNode.addLight(directionalLight);
        rootNode.addLight(ambientLight);
        mesh1 = new Sphere(13, 13, 0.4f, true, false);
        mesh2 = new Box(0.4f, 0.4f, 0.4f);
        
        materials[0] = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        materials[0].setBoolean("UseInstancing", INSTANCING);
        materials[0].setColor("Diffuse", ColorRGBA.White);
        materials[0].setColor("Ambient", ColorRGBA.Gray);
        materials[0].setBoolean("UseMaterialColors", true);
        
        materials[1] = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        materials[1].setBoolean("UseInstancing", INSTANCING);
        materials[1].setColor("Diffuse", ColorRGBA.White);
        materials[1].setColor("Ambient", ColorRGBA.Gray);
        materials[1].setBoolean("UseMaterialColors", true);

        instancedNode = new InstancedNode("instanced_node");
        rootNode.attachChild(instancedNode);
        
        int extent = 30;
        
        for (int y = -extent; y < extent; y++) {
            for (int x = -extent; x < extent; x++) {
                Geometry instance = createInstance(x, y);
                instance.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
                instancedNode.attachChild(instance);
            }
        }
        
        if (INSTANCING) {
            ((InstancedNode)instancedNode).instance();
            //instancedNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        }
        
        cam.setLocation(new Vector3f(38.373516f, 6.689055f, 38.482082f));
        cam.setRotation(new Quaternion(-0.04004206f, 0.918326f, -0.096310444f, -0.38183528f));
        flyCam.setMoveSpeed(45);
        flyCam.setEnabled(true);
        
        DirectionalLightShadowFilter directionalLightShadowFilter = 
                new DirectionalLightShadowFilter(assetManager, 1024, 2);
        directionalLightShadowFilter.setLight(directionalLight);
        directionalLightShadowFilter.setEnabled(true);
        directionalLightShadowFilter.setLambda(0.55f);
        directionalLightShadowFilter.setShadowIntensity(1f);
        fpp.addFilter(directionalLightShadowFilter);
        viewPort.addProcessor(fpp);
    }
    
    @Override
    public void simpleUpdate(float tpf) {
    }
}

When INSTANCING = false, everything works fine

However with INSTANCING = true there are no shadows

Also I tried to write instancedNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
And this led to

Is it normal? Or maybe I am missing something?

I’ll check. I guess there is a couple of missing stuff in the Shadow shader to work with instancing.

1 Like

I fixed this on master.
Note that you have to do

 instancedNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);

in order for it to work properly.

4 Likes