Shadows on transparent quad

Hey guys,
I know that there have already been some topics on this but I cannot get this to work. I have grass which consists of multiple quads with partly transparent png textures. I now want them to receive shadows and I built a simple testcase for that. This is the code:

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.shape.Quad;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.util.BufferUtils;
import com.jme3.util.TangentBinormalGenerator;

/**
 * test
 *
 * @author normenhansen
 */
public class Main extends SimpleApplication {

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

    @Override
    public void simpleInitApp() {
        
        cam.setLocation(new Vector3f(0,30,0));
        flyCam.setMoveSpeed(50);
        
        Spatial terrain = assetManager.loadModel("Scenes/newScene.j3o");
        rootNode.attachChild(terrain);

        /**
         * A white ambient light source.
         */
        AmbientLight ambient = new AmbientLight();
        ambient.setColor(ColorRGBA.White);
        rootNode.addLight(ambient);

        /**
         * A white, directional light source
         */
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);

        /* this shadow needs a directional light */
        DirectionalLightShadowRenderer dlsr = new DirectionalLightShadowRenderer(assetManager, 1024, 2);
        dlsr.setLight(sun);
        viewPort.addProcessor(dlsr);

        rootNode.attachChild(getGrassNode());
        Node n = getGrassNode();
        n.setLocalTranslation(-18.131676f, 0.0f, -18.806698f);
        rootNode.attachChild(n);
    }
    
    private Node getGrassNode() {
        Quad quad = new Quad(5, 5);
        Node grassNode = new Node();
        Geometry geom = new Geometry("grass", quad);
        float[] normals = new float[]{0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0};
        geom.getMesh().setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
        geom.setQueueBucket(RenderQueue.Bucket.Transparent);
        geom.setShadowMode(RenderQueue.ShadowMode.Receive);
        grassNode.attachChild(geom);
        grassNode.setShadowMode(RenderQueue.ShadowMode.Receive);
        grassNode.setCullHint(Spatial.CullHint.Dynamic);
        
        
        Material grassMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
//        grassMat.setColor("Diffuse", ColorRGBA.Green);
//        grassMat.setColor("Ambient", ColorRGBA.Green);
//        grassMat.setBoolean("UseMaterialColors", true);
        grassMat.setTexture("DiffuseMap", assetManager.loadTexture("Textures/billboardGrass.png"));
        grassMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        grassMat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
        grassMat.setFloat("AlphaDiscardThreshold", .5f);
        
        grassNode.setMaterial(grassMat);
        
        grassNode.setLocalTranslation(-18.131676f, 0.0f, -10.806698f);
        
        return grassNode;
    }

    @Override
    public void simpleUpdate(float tpf) {
        //TODO: add update code
    }

    @Override
    public void simpleRender(RenderManager rm) {
        //TODO: add render code
    }
}

So I get different results, but not the one I want to achieve. First I tested it without the texture:

That’s the code for that, looking fine so far:

Material grassMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        grassMat.setColor("Diffuse", ColorRGBA.Green);
        grassMat.setColor("Ambient", ColorRGBA.Green);
        grassMat.setBoolean("UseMaterialColors", true);
//        grassMat.setTexture("DiffuseMap", assetManager.loadTexture("Textures/billboardGrass.png"));
        grassMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        grassMat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
//        grassMat.setFloat("AlphaDiscardThreshold", .5f);

Now as soon as I set the AlphaDiscardThreshold to any number:

The shadow is somehow not applied once I set the AlphaDiscardThreshold…

Okay, let’s try it with a texture.

Material grassMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
//        grassMat.setColor("Diffuse", ColorRGBA.Green);
//        grassMat.setColor("Ambient", ColorRGBA.Green);
//        grassMat.setBoolean("UseMaterialColors", true);
        grassMat.setTexture("DiffuseMap", assetManager.loadTexture("Textures/billboardGrass.png"));
        grassMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        grassMat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
//        grassMat.setFloat("AlphaDiscardThreshold", .5f);

That’s not looking good. With AlphaDiscardThreshold set:

Looking okay, but the shadow is not applied at all - the same when I use no texture. How can I have a result as in the last image but with a visible shadow on the quad?

1 Like

That’s a really weird issue.
I tried your quad in the testTransparentShadows test case I have the issue indeed. Though for me it’s not related to the AlphaDiscardThreshold.
It seems that the back face of the quad don’t recieve shadows and that the quad can’t cast shadows on itself.
All of this seems to be related to the normals of the quad, even with the standard normals of a quad. If you want proper lighting on your grass blades you’ll need to have proper normals. A quad cannot be a good candidate for that, you’re gonna need a 2 sided quad with normals perpendicular to each side.

What you can try is to use the DirectionalShadowFilter instead of the renderer, it does work on the back face, but displays the exact same shadows as the front face (which is wrong but might be unoticable if you have proper lighting).

Okay I’ll try these things asap and will keep you informed. Thank you!

Okay with the DirectionalShadowFilter, I don’t even get proper terrain shadows:

They should be much darker, as in the first post. This is the code:

FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(assetManager, 1024, 2);
        dlsf.setLight(sun);
        dlsf.setShadowIntensity(1f);
        fpp.addFilter(dlsf);
        viewPort.addProcessor(fpp);

Now I tried setting up the normals.

private Node getGrassNode() {
        Quad quad = new Quad(5, 5);
        Node grassNode = new Node();
        Geometry geom = new Geometry("grass", quad);
        float[] normals = new float[]{-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0};
        geom.getMesh().setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(normals));  //When I only set these normals, the front darker than the back, no matter if the grass in in the shadow                       //or not
        geom.setQueueBucket(RenderQueue.Bucket.Transparent);
        geom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        grassNode.attachChild(geom);
        
        Geometry geom2 = geom.clone();
        geom2.rotate(0, 180 * FastMath.DEG_TO_RAD, 0);
        geom2.move(5, 0, 0.1f);
        float[] normals2 = new float[]{1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0};
        geom2.getMesh().setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(normals2));  //When I only set these normals, the back is darker than the front
        grassNode.attachChild(geom2);
        
        grassNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        grassNode.setCullHint(Spatial.CullHint.Dynamic);

        Material grassMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        grassMat.setTexture("DiffuseMap", assetManager.loadTexture("Textures/billboardGrass.png"));
        grassMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        grassMat.setFloat("AlphaDiscardThreshold", .7f);

        grassNode.setMaterial(grassMat);

        grassNode.setLocalTranslation(-18.131676f, 0.0f, -10.806698f);

        return grassNode;
    }

I somehow even think that the dark side of the one which is not in the shadow is darker… And If I set the normals for the back and the front, the back is once again darker. If I set no normals, I get the same result as in the following images. Here are screenshots when I sat all the normals like in the code above:


Setting all the normals in the “up” direction made no side darker at all.

I’m not even sure if I should change the normals from pointing upwards because the grass itself will obviously be not just one quad and will cause lgihting issues otherwise… But I don’t thinkt that making it appear darker in shadow is possible then… I of course might just use a darker texture in shadowy arears; would there be any way to find out if the object currentl rendered is in shadow?