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!

I think I found my answer in the Migration Notes

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

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

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

[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.]

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.

Bias is unrelated to pixelation.

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.

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