Shadow Pixelation Issue in Three.js 0.182.0

RAVENFALL (Three.js FPS game) uses 0.181.0 and shadows look decent

RAVENFALL (Three.js FPS game) - 0.182.0 Pixelated Shadows is the exact same code with two lines changed in the importmap, pointing to version 0.182.0 for “three” and “three/addons/”

There is extreme pixelation occurring:

Does anyone know why this happens?
Am I doing something wrong?

The setup is

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
...
const sun = new THREE.DirectionalLight(0xffc080, 1.8); // cool moonlight color
sun.position.set(MOON_DIR.x * 100, MOON_DIR.y * 100, MOON_DIR.z * 100);
sun.castShadow = true;
sun.shadow.mapSize.set(4096, 4096);
sun.shadow.intensity = 1;
sun.shadow.normalBias = 0.005;
sun.shadow.bias = -0.0001;
sun.shadow.autoUpdate = true;
sun.shadowCameraVisible = true;
sun.shadow.camera.near = 1;
sun.shadow.camera.far = 300;
...
sun.shadow.camera.left = -SHADOW_CONFIG.FRUSTUM_SIZE_DEFAULT; // 50
sun.shadow.camera.right = SHADOW_CONFIG.FRUSTUM_SIZE_DEFAULT;
sun.shadow.camera.top = SHADOW_CONFIG.FRUSTUM_SIZE_DEFAULT;
sun.shadow.camera.bottom = -SHADOW_CONFIG.FRUSTUM_SIZE_DEFAULT;
sun.shadow.camera.zoom = 0.5; // it's not from this, I tried changing/removing
sun.shadow.camera.updateProjectionMatrix();
...
function render() {
  ...
  updateSunShadow(dt); // to move the shadow dynamically with the camera
  updateShadowFrustum();
  ...
}

Thanks in advance!

2 Likes

I think I found my answer in the Migration Notes

PCFSoftShadowMap is now deprecated. Use PCFShadowMap which is now soft as well.”

1 Like

Consider to report this issue at GitHub. We should investigate why the shadow becomes so pixelated.

2 Likes

@Mugen87 thanks. Done. Also created a simpler, dedicated CodePen for easy reproduction. Thanks for looking into this!

2 Likes

[EDIT: 12/21] - The following statements were made under the mistaken assumption that the changes applied to WebGPU. Nevertheless, it appears that, after all the changes to shadows, the bias number of -0.0001 now also works best for the WebGPU DirectionalLight.]

I also missed this change and am also getting the same pixelation when making the required switch to PCFShadowMap. [EDIT: In WebGPU, making this change caused the pixelation. So don’t do this.]

I thought I might fix it by changing the bias value, but your bias value of -0.0001 seems to work perfectly (I previously used -0.0005, but that now creates a gap between where the object intersects the ground and the start of the shadow).

And changes in bias value did not appear to have any effect on the pixelation.

[EDIT: Deleted irrelevant statement.]

1 Like

There is a bug in the renderer right now. It does not automatically switch from PCFSoftShadowMap to PCFShadowMap. On current dev, the error is already fixed.

4 Likes

Bias is unrelated to pixelation.

1 Like

Thanks for clarifying that. The reason I was messing with bias is that it can have some pretty odd effects - including some which can mimic pixelation problems.

I made a mistake. For some reason, I was thinking that RAVENFALL was a WebGPU game. It is not.

Is PCFSoftShadowMap also deprecated for WebGPU?

[EDIT- 12/21: Deleted additional questions that belong in a separate discussion.]

No, this is done only for WebGLRenderer right now.

1 Like

A bit late to the party, but I thought I’d chip in.

I use a lot of enums in meep and shade, here’s my approach to help the API user, here’s a real function from shade:



/**
 *
 * @param {ShadeMaterial[]} output Where the output goes
 * @param {number} output_offset Where to start writing
 * @param {TransparencyMode} alpha_mode
 * @param {ShadeDrawMode} draw_mode
 * @param {ShadeDrawSide} draw_side
 * @param {ShadeMaterial[]} materials inputs to filter
 * @param {number} count number of inputs to read
 * @returns {number} number of materials that were filtered
 */
export function filter_materials_by_bucket(
    output,
    output_offset,
    alpha_mode,
    draw_mode,
    draw_side,
    materials,
    count
) {
    assert.enum(alpha_mode, TransparencyMode, 'alpha_mode');
    assert.enum(draw_mode, ShadeDrawMode, 'draw_mode');
    assert.enum(draw_side, ShadeDrawSide, 'draw_side');
    assert.isNonNegativeInteger(count, 'count');
    assert.isArray(materials, 'materials');
    assert.isNonNegativeInteger(output_offset, 'output_offset');
    assert.isArray(output, 'output');
....

The let’s look at just one enum here, the TransparencyMode

assert.enum(alpha_mode, TransparencyMode, 'alpha_mode');

The enum is defined as

/**
 *
 * @enum {number}
 */
export const TransparencyMode = {
    Opaque: 0,
    AlphaTested: 1,
    /**
     * Blended
     */
    Transparent: 2,
}

So if the user supplies anything other than 0,1 or 2, they get an asertion error, let’s assume the user provided value 3, they would get an error:

alpha_mode(=3) is not a valid enumerable value, valid values are: [0, 1, 2]

Assertions can be compiled out, most packaging modules out there support those, here’s an example for vite:

// vite.config.js
import strip from '@rollup/plugin-strip';
export default defineConfig({
    plugins: [{ ...strip(), apply: 'build' }]
});

I remember when three.js used to do more input validation, with assertions you get best of both worlds - more validation and no production overhead.

It’s a different philosophy, but good error checking/messaing is a valuable thing in an API.


Well, there’s also TypeScript, but that’s for the weak of will


Just in case anyone find it confusing, the assertions here are from @woosh/meep-engine/src/core/assert

3 Likes