PointLightShadowRenderer artifacts

Hi!

I have decided to use shadows in my application and I have noticed some black rectangles, where they should not be. Looks like there is a bug in the PointLightShadowRenderer, or I use it incorrectly. I will provide a simple test (JME 3.2):

package jme3test.light;

import com.jme3.app.SimpleApplication;
import com.jme3.light.PointLight;
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.shadow.PointLightShadowRenderer;

public class TestPointLightShadow extends SimpleApplication {
    private final boolean chaseLight = true;
    private final float offset = -20f;

    private PointLight pointLight;
    private Geometry box;
    private float x = offset;

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

    @Override
    public void simpleInitApp() {
        box = new Geometry("Box", new Box(1f, 1f, 1f));
        Material material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        material.setColor("Diffuse", ColorRGBA.White);
        box.setMaterial(material);
        box.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        box.setLocalTranslation(x, 0, 0);
        rootNode.attachChild(box);

        pointLight = new PointLight();
        pointLight.setColor(ColorRGBA.White.mult(1.8f));
        pointLight.setRadius(30f);
        rootNode.addLight(pointLight);
        pointLight.setPosition(new Vector3f(offset - 2f, 0, 0));

        cam.setLocation(new Vector3f(offset - 4f, 0, 0));
        cam.lookAtDirection(Vector3f.UNIT_X, Vector3f.UNIT_Y);
        flyCam.setMoveSpeed(1f);

        PointLightShadowRenderer plsr = new PointLightShadowRenderer(assetManager, 1024);
        plsr.setLight(pointLight);
        viewPort.addProcessor(plsr);
    }

    @Override
    public void simpleUpdate(float tpf) {
        if (chaseLight) {
            pointLight.setPosition(cam.getLocation().add(2, 0, 0));
        } else {
            x -= tpf / 10f;
            box.setLocalTranslation(x, 0, 0);
        }
    }
}

Here we have a single white box with lighting material. There is a white point light in front of it. Camera is in front of the box (point light is between camera and box). Box casts and receives shadows. We use renderer (not filter) for shadows.

Test allows to run it in 2 modes: chaseLight=true, when point light chases the camera (try to get closer to the box to see the bug) and chaseLight=false, when box moves towards the camera (you will see bug automatically). In both cases, when point light is close to a box, there is a dark rectangle on the box. The closer the point light to the box surface, the smaller the rectangle. As we have only a single geometry in the scene, there should not be any shadow, but we have it from somewhere! Bug is reproduced, when shadow mode is CastAndReceive. It is not shown for other shadow modes.

Test also has offset. It just shifts the box, light and camera at the same distance. Bug is reproduced only if offset is within point light radius (a little bit less, I think it also depends on the box size). If to set offset to 30, bug will not be reproduced. It looks like PointLightShadowRenderer uses PointLight position using local space system instead of world one for some calculations, and casts box on top of itself.

Can you please confirm that this is a bug?

Thank you!

Running the test case you gave I didn’t see anything unusual. Can you provide a screenshot of what you are seeing that you think shouldn’t be there?

Blind shot - it is caused by shadowcam’s near frustum.
Blind shot 2 - you have ATI card and some old driver.

Here is a screenshot:

It looks like near frustum doesn’t cause it. I have added:

float aspectRatio = (float) settings.getWidth() / settings.getHeight();
cam.setFrustumPerspective(60f, aspectRatio, 0.005f, 100f);

This is not a clipping issue. Also shadow is the same in size, if to change near clipping range, if to test with the same distance between point light and box.

I see this bug on desktop NVIDIA GTX 1070 and laptop with AMD Mobility Radeon HD 4570. I also noticed such strange rectangles 1 year ago, when I had NVIDIA GTX 760.

Update
Here is a screenshot, if camera is rotated and shadow intensity is decreased:

cam.lookAtDirection(Vector3f.UNIT_X, new Vector3f(0,1,1));
plsr.setShadowIntensity(0.3f);

We can see that strange rectangle’s color became brighter, so it really looks like a shadow. We also see that this shadow is along the box axis. If it were the issue with the frustum plane, then it would be along the camera axis.

Here is another example. This time camera is not rotated (up vector is (0, 1f, 0)), but instead a box is rotated):

box.rotate(0.5f, 0, 0);
plsr.setShadowIntensity(0.3f);

We can see that this time shadow is not rotated. It really looks like that PointLightShadowRenderer does not take into account local transforms.

Update 2
@FrozenShade, I see you told shadowcam’s near frustum. I have just looked into PointLightShadowRenderer and I see that it uses cloned camera to calculate frustum. I see that near frustum distance is also cloned. If to stretch the box, shadow will stay square and won’t be stretched, so looks like bug is connected with shadowCams.

I have experimented a little bit more and found out that such shadow artifact is visible on surface only if it is very close to perpendicular to X axis. If to rotate a box a little bit around Y axis, then shadow artifact will be absent. If to rotate it very close to 0, then picture will resemble z-fighting:

box.rotate(0, 0.0001f, 0);

Update 3
I also see such shadow artifact, when spatial is moving (setLocalTranslation during every update). This time offset and rotating the spatial does not help to avoid such artifact. It is visible even if spatial distance from the center of the World is greater than point light radius. No artifact, when spatial is added to the scene. But when it starts moving, artifact is visible.

I reproduce the issue.
Though I’m not sure what your scene is supposed to show.
To me you shouldn’t have any shadow projected on the Cube.

@nehon, I have many geometries, which cast and receive shadows in my real application. Shadows are received correctly in the scene (exception is shadow for instanced geometries, but it is another story), but I also noticed additional shadows (those strange rectangles), which should not be there. So I created the simplest test case, which shows those strange shadows I have.

And I agree with you that there shouldn’t be any shadow projected on the cube, but it is projected for some reason.

Seems that the problem is the near clip plan of the shadow cams…
I’ll update on what I find.

@nehon, thank you for investigating the issue!

I see such line:

shadowCams[i].setFrustumPerspective(90f, 1f, 0.1f, light.getRadius());

Near clipping plane range is 0.1f. And shadow artifact appears, if point light position is more than 0.1f from the cube’s face. There is no shadow, if this distance is less than 0.1f.

Update
Increasing polygon offset units for the PreShadow technique fixes issue in this test case. From PolyOffset 5 3 to PolyOffset 5 70. The bigger units number the later shadow artifact appears. But it doesn’t fix the issue in all cases.

Update 2
With plsr.displayDebug(); all 6 shadow maps are white, which looks like scene is empty or objects are far away from the point light. I don’t think that this is good, because cube should write to depth buffer (I guess PreShadow is created for such purpose).

Update 3
I continue digging into this issue, and now I think I have understanding of the effect. Let me explain with details. Everything below will be about fragment shader for the PostShadow technique for the point map shadows.

getPointLightShadows function chooses 1 of 6 shadow maps, where fragment is located. Shadow map is a depth buffer with the origin of point light location. When a shadow map is selected, a test is made, if fragment is behind something: if fragment’s z is bigger, than shadow map’s z, then something covers the fragment, and we need to paint shadow there. And it works fine, if to create scene with many geometries, but there is a nuance with fragments, which are not covered by anything.

If fragment’s z is the same as shadow map’s z, then we don’t need to paint shadow. But, if there is a very small difference, then we will see a z-fighting between shadow and original material color. I see glPolygonOffset is used, when depth buffer (shadow maps) is generated. We also have shadow mode CastAndReceive, so the same geometry is an occluder and a shadow receiver at the same time. If depth buffer values are shifted a little bit (because of glPolygonOffset or because of any other reason), and if geometry face is coplanar (or close) with the shadow map near/far frustum plane, then the closest to the point light fragments might become in the false shadow.

This also answers the question why this artifact shadow is square, even if original geometry face is not. This is because each of the 6 shadow maps is a square. So that effect in the example is because all the fragments within a single shadow map tell, that they are behind own face, and we see an artifact shadow.

I understand why glPolygonOffset is used. There is a “shadow acne” without it. Maybe we need to think about other solutions to fight it?

What about 6 white shadow maps with plsr.displayDebug();, I think it is a separate bug, because I see shadows, if to test scene with many geometries. So depth buffer has correct values (with some shift because of glPolygonOffset). Just renderer doesn’t present shadow maps correctly in those 6 squares in the bottom of the screen (maybe it reads from the depth buffer, when it is not yet filled).

Update 4
Thinking about polygon offset for the 2nd time, I have come to conclusion, that it is not that bad as I first thought. It not only helps to avoid shadow acne, but it also helps to avoid the problem described in this topic (shadow cast and received by the same face). Just need to use really big polygon offset. The question is only how big, and what a proportion between factor and units should be?

And why before I mentioned that polygon offset didn’t fix all issues, is that I saw white “shadow” artifacts. I have checked this case closer and found out, that it was caused by point light, when it was inside the geometry with shadow mode CastAndReceive. One front face was bright. I think I won’t use such cases in real application. Besides this situation, big positive polygon offset helps to deal with the effect described in first post in all cases I tested.

Thanks that’s a good analysis of the issue indeed.

well… I’d kill to find one…I’m afraid there isn’t.

I think this is a bug with the debug display… never dug into it though, since the shadows are still working.

This is the issue… Polygon ofsset is not a silver bullet… on one hand it avoids those issues, on the other hand it offsets the shadows… that’s why you have very visible shadow displacements ont cubes for example where the shadow seems to be a bit off from its geometry… and of course…the bigger the offset the less you have acne…but the more you have off shadows…

All those issues occurs when the scene have objects that have big plane areas that are axis aligned. It’s not easy to get by with other types of lights,but the point light uses 6 cams that are axis aligned and when the light direction is perpendicular to the surface… it gives those artifacts…

Self shadowing is a pain in the A…

though, all this made me realized we don’t dynamically compute the near and far planes for the shadow camera depending on the objects in the frustum.
I implemented something that somehow mitigate the issue… but the artifact is still there unfortunately… Less acne, but still some flickering shadows…

Do you mean increase shadow cams’ near plane and decrease far plane within allowed range [minimum_allowed_near_distance, point_light_range], while all the geometries are still kept in the frustum, so we will have less z-fighting, because far/near coefficient will be smaller, and z-buffer precision will increase? Sounds like a good idea.

yes I mean this.
For PSSM with directional light we have something that crops the light matrix to fit the area (though I’m not sure it changes the near far, I will check this). But for point light we have nothing…
It’s pretty easy since the cams are axis aligned and the bounding boxes are too… so computing the closest geom and the farthest in the light frustum is almost trivial.
Again it doesn’t fix the issue, but it should reduce shadow acne due to depth buffer poor precision…

But, if such logic is implemented, think about next situation: when point light is approaching the geometry, you will need to dynamically decrease shadow cam’s near plane, and z-buffer precision will be decreasing during the process, and some shadow artifacts might become visible. You can’t decrease near plane to 0. It will be stopped at some minimum allowed number, like 0.1f anyway. Because such situations can happen, it would be nice to care about the worst situation and avoid shadow acne for it. And current static small near-plane is the worst situation.

Yes, still not a silver bullet :wink:

Big polyoffset value will end with peterpanning. To avoid selfshadowing (moire patterns on front faces) you can exclude front faces from rendering. But be careful - JME is drawing shadowas instead of not drawing pixels covered with shadow - any additional light will expose any shadow artifacts on the other side of your geometry.
.

1 Like

You are right. I see shifted shadow, if to use really big polygon offset, so geometry is kind of floating. As I understand effect will be worse, if to use thin geometries.

see this article for common shadow mapping issues https://msdn.microsoft.com/en-us/library/windows/desktop/ee416324(v=vs.85).aspx
It’s nice at describing the issues, not so nice at giving solutions…

1 Like

I see JME uses modulate blend mode to mix rendered scene with rendered shadows. Basically it is dest = src * (1.0 - shadowInCurrentFragmentTakingIntoAccountShadowIntensity). All the fragments (in shadow and not in the shadow) are “modulated”.

By excluding front faces from rendering, I think you mean pre-shadow rendering, when depth buffer is filled (when shadow maps are created). If roughly speaking front faces are absent in the depth buffer, they won’t create shadow from own face.

Thank you for the link! The more I understand the nature of shadow artifacts, the less I understand the nature of that black rectangle. It is clear that it appears, because the closest to the point light front face has similar depth with the shadow map depth. But what I don’t understand is why such effect is seen only, when point light is close to the face.

Shadow map is created from the center of the point light. Depth precision is better near near clipping plane, so the closer the point light to the face, the more precise shadow map depth will be on the surface, and the less chance for the shadow acne (if we use some moderate polyoffset). But we see completely opposite situation: self-shadow rectangle is seen only, when point light is close to the face, and it is absent, when it is far. Very big polyoffset fixes the problem, but it creates a peter-panning.

Moderate polyoffset fixes regular shadow acne, but it doesn’t fix it, if shadow camera’s near/far frustum is coplanar with the front face.

That’s because of the polygon offset.
It shifts the polygon when the slope is important and it doesn’t when there is no slope. So when the light is perpendicular to the surface it’s not shifted (ar very slightly). You can increase the amount of shift even there… but then you increase the peter panning effect.