Skip to content

Conversation

@mrdoob
Copy link
Owner

@mrdoob mrdoob commented Nov 18, 2025

Related issue: #5554 #8577 #32180

This PR modernizes the WebGLRenderer shadow mapping with several significant improvements:

Before After
Screenshot 2025-11-18 at 22 04 20 Screenshot 2025-11-18 at 22 04 14

Major Changes

1. Native Cube Depth Texture Support for PointLight Shadows

  • Added new CubeDepthTexture class for native cube shadow maps
  • Removed legacy 2D viewport-based cube rendering (old xzXZ/yY layout)
  • Updated PointLightShadow to render directly to cube faces instead of 2D viewports
  • Updated WebGLTextures to support cube depth texture binding

2. Removed RGBA Depth Packing

  • Now using native depth textures (samplerShadow/samplerCubeShadow for PCF, samples/samplerCube for Basic)
  • Renamed distanceRGBA.glsl.jsdistance.glsl.js
  • Removed all cubeToUV() and RGBA packing code from shaders
  • Updated ShadowMapViewer and related examples to work with native depth

3. Vogel Disk + Interleaved Gradient Noise (IGN) Sampling

  • Implemented modern soft shadow sampling technique in shadowmap_pars_fragment.glsl.js
  • Uses Vogel disk for uniform circular distribution
  • IGN provides per-pixel noise to rotate sampling patterns
  • Hardware PCF with LinearFilter gives 4-tap filtering per sample
  • 5 samples × 4 taps = effectively 20 filtered taps with better quality
  • Removed PCFSoftShadowMap as the new PCFShadowMap is already soft

4. VSM Shadow Map Improvements

  • Fixed VSM shadow map type switching errors (proper texture disposal)
  • Added VSM/PointLight incompatibility handling at multiple levels:
    • JavaScript: Skip shadow map creation with console warning
    • Shader declarations: Exclude VSM-specific uniforms for PointLights
    • Shader usage: Only call shadow functions when appropriate
  • Proper depth texture cleanup when switching shadow map types

5. WebGL Optimizations

  • WebGLUniforms: Prioritize shadow samplers (SAMPLER_2D_SHADOW, SAMPLER_CUBE_SHADOW) for Adreno GPUs
  • WebGLUniforms: Support reversed depth buffer compare functions
  • WebGLLights: Added shadowCameraNear and shadowCameraFar uniforms for PointLight shadows

Files Changed

  • New: src/textures/CubeDepthTexture.js
  • Renamed: distanceRGBA.glsl.jsdistance.glsl.js
  • Major rewrites: shadowmap_pars_fragment.glsl.js, WebGLShadowMap.js
  • Significant updates: PointLightShadow.js, WebGLTextures.js, WebGLUniforms.js

Testing

Tested with DirectionalLight, SpotLight, and PointLight shadows across BasicShadowMap, PCFShadowMap, and VSMShadowMap types. Shadow map type switching works correctly, with appropriate warnings for VSM/PointLight combinations.

(Made with VS Code and Claude Sonnet 4.5)

@github-actions
Copy link

github-actions bot commented Nov 18, 2025

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 356.18
86.47
357.1
86.71
+914 B
+241 B
WebGPU 616.43
173.08
616.44
173.09
+17 B
+5 B
WebGPU Nodes 615.03
172.83
615.05
172.84
+17 B
+5 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 488.05
121.29
489.22
121.61
+1.18 kB
+316 B
WebGPU 688.66
188.99
688.5
188.96
-163 B
-29 B
WebGPU Nodes 630.38
172.22
630.22
172.19
-163 B
-28 B

@mrdoob mrdoob added this to the r182 milestone Nov 18, 2025
@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 18, 2025

Now using native depth textures (samplerShadow/samplerCubeShadow for PCF, samples/samplerCube for Basic)

We must be careful with this change. We did the same in WebGPURenderer and frequently see now precision issues with shadow maps, see #31885. It's not clear yet why the depth texture approach is more error-prone compared to the RGBA packing.

@mrdoob
Copy link
Owner Author

mrdoob commented Nov 18, 2025

This test now renders 2x faster on a Pixel 3a (8fps vs 18fps).

@mrdoob
Copy link
Owner Author

mrdoob commented Nov 18, 2025

@Mugen87

We must be careful with this change. We did the same in WebGPURenderer and frequently see now precision issues with shadow maps, see #31885. It's not clear yet why the depth texture approach is more error-prone compared to the RGBA packing.

Please test this PR in as many devices as you can 🙏

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 19, 2025

For testing: https://rawcdn.githack.com/mrdoob/three.js/74bb0b15deffdf7d6ab56180a049dc9f9b1f297d/examples/index.html

@mrdoob
Copy link
Owner Author

mrdoob commented Nov 19, 2025

There's a problem with BasicDepthPacking...

export const BasicDepthPacking = 3200;

The "Basic" depth does 1.0 - fragCoordZ:

#if DEPTH_PACKING == 3200
gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );

I suspect that's why this was needed.

But now webgl_shadowmap_viewer is failing because of it:

Screenshot 2025-11-19 at 18 23 16

Pretty annoying, but I'll probably have to add a new constant...

I wouldn't want to call it InvDepthPacking or something along the lines.

Asked Gemini and it suggested IdentityDepthPacking and RawDepthPacking...
RawDepthPacking is consistent with RawShaderMaterial but I'm really not a fan of Raw anymore...

So probably IdentityDepthPacking 😤

It's annoying that BasicDepthPacking is the default for DepthMaterial too.

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 19, 2025

Here are my findings:

  • macMini

    • No issues with Chrome and Safari
    • Firefox shows below warning in various examples using shadows e.g. webgl_shadowmap (visuals are okay though):
    • WebGL warning: drawElementsInstanced: Depth texture comparison requests (e.g. LINEAR) Filtering, but behavior is implementation-defined, and so on some systems will sometimes behave as NEAREST.

  • Pixel 8a

    • Firefox Mobile looks fine
    • Broken rendering with Chrome. Examples like webgl_animation_skinning_blending or webgl_animation_skinning_additive_blending do not render the character mesh anymore.
image

The following WebGL warnings are reported in the console per frame:

image
  • iPad (8th generation)

    • No issues with Safari
  • Windows 10 notebook (NVIDIA GPU)

    • No issues with Chrome

@mrdoob
Copy link
Owner Author

mrdoob commented Nov 19, 2025

Nice! The good news is that my girlfriend has a Pixel 8a somewhere, I'll investigate tomorrow.

@mrdoob
Copy link
Owner Author

mrdoob commented Nov 22, 2025

@Mugen87

Okay, I think it should work now. Can you test again?

https://rawcdn.githack.com/mrdoob/three.js/a27620493eb09af9e81e063d7b47f32e252a63b3/examples/index.html

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 22, 2025

The broken examples work on the Pixel again. I've also checked the browser console and it is free from errors/warnings as well. 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants