DirectionalLightShadowRenderer self-shadow bad rendering on low poly curved surfaces

Hi,

In the project I’m working on, I have a scene with multiple light sources and, among them, one directional light which is used as the light for the DirectionalLightShadowRenderer.

I’ve realized two different, although maybe related, rendering issues when the object has a low poligon count, it’s big enough and is curved when configured as CastAndReceive.

I’ve created a simple test to show it. The sphere on the left hast much more segments than the one on the right and this way the rendering is better looking although still buggy.

In the front side you can see some lines that matches the segments of the object:

In the back side, as it’s enlighted by other light sources, you see clearly some more artifacts. You can also see this artifacts if using unshaded materials instead:

The testcase is as follows:

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.shadow.EdgeFilteringMode;

public class BallTest extends SimpleApplication {

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

    @Override
    public void simpleInitApp() {
        
        flyCam.setDragToRotate(true);
        flyCam.setMoveSpeed(20);
        flyCam.setRotationSpeed(3);
        cam.setLocation(new Vector3f(0f, 60f, 60f));
        cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
        
        //Main light
        DirectionalLight l0=new DirectionalLight();
        l0.setColor(ColorRGBA.LightGray);
        l0.setDirection(new Vector3f(-0.5f, -0.5f, -0.5f).normalizeLocal());
        rootNode.addLight(l0);
        
        //Aditional light source to enlight shadowed side
        DirectionalLight l1=new DirectionalLight();
        l1.setColor(ColorRGBA.Yellow);
        l1.setDirection(new Vector3f(0.5f, -0.5f, 0.5f).normalizeLocal());
        rootNode.addLight(l1);
        
        Material matRedLight=assetManager.loadMaterial("Materials/RedLight.j3m");
        Material matRedUnshaded=assetManager.loadMaterial("Materials/RedUnshaded.j3m");
        
        Sphere s1=new Sphere(36,36,15);
        Geometry geom1=new Geometry("Sphere", s1);
        geom1.setMaterial(matRedLight);
        geom1.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        geom1.setLocalTranslation(16, 15, 0);
        rootNode.attachChild(geom1);
        
        Sphere s2=new Sphere(150,150,15);
        Geometry geom2=new Geometry("Sphere", s2);
        geom2.setMaterial(matRedLight);
        geom2.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        geom2.setLocalTranslation(-16, 15, 0);
        rootNode.attachChild(geom2);

        //Floor
        Box b = new Box(80, 1, 80);
        Geometry floor = new Geometry("Box", b);
        Material matFloor = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        matFloor.setColor("Color", ColorRGBA.Blue);
        floor.setMaterial(matFloor);
        floor.setShadowMode(RenderQueue.ShadowMode.Receive);
        rootNode.attachChild(floor);
        
        DirectionalLightShadowRenderer pssm=new DirectionalLightShadowRenderer(assetManager, 2048,4);
        pssm.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON);
        pssm.setLambda(0.3f);
        pssm.setLight(l0);
        viewPort.addProcessor(pssm);
        
    }
}

The material (RedLight.j3m):

Material RedLight : Common/MatDefs/Light/Lighting.j3md {
     MaterialParameters {
         UseMaterialColors : true
         Diffuse : 1.0 0.3 0.3 1.0
     }
    AdditionalRenderState {
    }
}

I’ve found a related topic (Issues with DirectionalLightShadowRenderer rendering shadows - #6 by nehon) and tested the recommendations in it like reducing lambda, changing filtering mode and so with no luck. All of them lead to this issues although some are a little better

Thanks

2 Likes

I’ve been getting deeper into this and I’ve realized it’s related to the typical acne issue in shadow mapping. I’ve partly implemented a per pixel dynamic bias in the post shadow frag like the one described at Shadow Mapping with Android OpenGL ES 2 - CodeProject and Tutorial 16 : Shadow mapping and the render issues were solved:

This fix generated some small peter panning issue. I’m not a good shader developer, so any help to fix this issue is appreciated. Also maybe jme3 has some mechanisms to solve this stuff already that I don’t know about…

EDIT: I’m using current jme3 master + the modified post shadow frag. Maybe that’s the reason why the image renders the colors a little lighter :confused:

3 Likes

In master gamma defaults to true… so check your settings dialog to see if that’s why the colors got washed out. (ie: set it to false again to see if the colors go back to normal)

If that’s the case then you may be able to fix the issue and leave gamma on by setting srgb color… which I think would have to be done in code.

If it’s not gamma then I don’t know.

Yes, the color is because gamma correction, I didn’t notice the option being set. Thanks :wink:

Back to the topic, the render is the same (no shadow artifacts on round surfaces) but with previous color

I’m interested in this issue. Do the same shadow artifacts appear if you use JME v3.2.4 instead of master?

Yes, exact same artifacts in both, in fact the first post screenshots were done running 3.2.4 but I think they were not noticed before because most test scenes uses small objects with enough polygons and just one light source.

The quick dynamic bias solution I implemented is not good enough, makes even more artifacts on the sides and backs of the shadowed spheres (the render in the second post was done having backface shadows to false so they’re not shown there).

I found this paper (http://jcgt.org/published/0003/04/08/paper-lowres.pdf) which looks promising and I’m trying to port the sample implementation to jme. In fact my goal is to port this one https://dspace5.zcu.cz/bitstream/11025/29520/1/Ehm.pdf which is based in the previous.

I have it partly implemented and it has good looking results but still with some artifacts.


In this screenshot you can see a little peter panning artifact on little cube back face. This happens when the shadowmap is lighter


In this screenshot there’s some acne on low poly big sphere on the right. This happens when the shadowmap is darker

Also, do you know why the shadow maps change from lighter to darker and back? I mean, the light direction is the same and both near and far of the shadow camera generating the map is constant although I move the viewport camera :confused:

2 Likes

No, I don’t know why.

Are you referring to the map in the little window in the lower left? It changes because of scene depth.

I know it depends on the camera depth, but what I was curious about is that, being the same scene, same light, and almost not moving the main camera why the shadow camera changes the projection matrix so much

Is this a shape way in the background:
image