Shadow artifacts with WebGPURenderer VSMShadowMap

I can’t get VSMShadowMap to work right (using WebGPURenderer).

When I use PCFSoftShadowMap the scene looks like the following (white line is a light helper):

Changes to light.shadow.radius and light.shadow.blurSamples seem to have no effect on the shadow.

I would like softer/blurrier/fuzzier shadows. When I switch to VSMShadowMap, I get something like the following:

That’s with shadow.radius 30 and shadow.blurSamples 16, size 1024x1024. The wall does not cast shadows, only receives shadows, so it can’t be acne.

When I change radius to 10 and samples to 8, I get:

Radius 4 samples 2:

The shadow isn’t looking right. But also regardless of the radius/samples, there’s this gradient banding.

Any ideas why this is? I still need to make a standalone reproduction.

Oh, the VSMShadowMap docs state:

When using VSMShadowMap all shadow receivers will also cast shadows.

So castShadow not being true doesn’t matter then. I wonder if this has anything to do with it. Why does PCFSoftShadowMap look drastically better?

EDIT: Ah! That banding was shadow acne. I didn’t realize enabling VSM shadows forced shadow receivers to also cast shadow even if castShadow is false, so I had assumed the wall was not casting shadows on itself, leading me to believe it was some other issue!

To fix the banding, I had to adjust the light.shadow.camera.near/far values to tighten up the shadow camera frustum, then I could remove tiny amounts of acne with small shadow.bias values. The frustum near/far distance was previously way too large, which happened to work fine with PCFSoft shadows.

However there’s still a “shadow” at the edge to the shadow map that I’m not sure how to get rid of:

(other lines are light/camera helpers)

How do we get rid of that edge-of-shadow-map artifact?

Turning down the resolution from 1024 to 512 (and adjusting blur/samples to get a desired shadow), I can now see three edges of the shadow map:

How do we get rid of those?

EDIT: same problem here: Hide line from Shadow - #10 by Chaser_Code

If I increase shadow.bias to a fairly larger number like -0.1, I get less shadow-map-edge-lines, but the whole shadow is almost gone, so that isn’t working:

It’s like there is a massive depth delta at the shadow map edges.

Is there some way to hook up TSL, re-using VSM nodes, but adding some logic to fade out the edges of the shadow map?

A solution for the shadow outline artifact is to patch ShadowNode.prototype.setupShadowFilter to fade out shadows around the edges of the shadow map area, like so:

			const originalSetupShadowFilter = THREE.ShadowNode.prototype.setupShadowFilter;

			THREE.ShadowNode.prototype.setupShadowFilter = function ( builder, inputs ) {

				// portion of shadow map UV space along edges to fade
				const fadePortion = 0.05;

				const base = originalSetupShadowFilter.call( this, builder, inputs );
				const uv = inputs.shadowCoord.xy;

				// Distance to nearest edge of the shadow UV box
				const edgeDist = min( min( uv.x, uv.x.oneMinus() ), min( uv.y, uv.y.oneMinus() ) );

				// Fade: 1 at the very edge, 0 inside (only if in a shadow)
				const fade = sub( 1, smoothstep( 0, fadePortion, edgeDist ) );
				return mix( base, 1, fade );

			};

1 Like

There was also a bug, that just got fixed today, that was coincidentally causing excessive banding compared to WebGLRenderer:

It turns out the outline artifact is a bug in Safari only!

2 Likes