Skip to content

WebGPURenderer: Introduce Shadow Map Array #30830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Apr 3, 2025

Conversation

RenaudRohlinger
Copy link
Collaborator

@RenaudRohlinger RenaudRohlinger commented Mar 31, 2025

By combining RenderBundle, ArrayCamera, RenderTargetArray, and a new DepthArrayTexture and FrustumArray, this PR opens the door to rendering multiple shadow passes in a single, highly performant render.

I’ve added a new example along with a TileShadowNode to demonstrate a foundational use case that takes advantage of this feature. In the example, the scene is divided into four sections, enabling a 16K resolution shadow map at nearly the same cost as 8K. This technique bypasses the 8K resolution limit, improves performance, and avoids the 16-sampler bind limit.
image

Looking ahead, it would be great to update nodes like CSM to leverage this new feature, especially since CSM already uses multiple FBOs at the same resolution, making it a perfect match. We could even implement a Tiled CSM system for open-world scenes, now that we can scale up to a 2048 shadow map limit per program.


TODO:

  • WebGL Support
  • Move TileShadow Helpers to a new TileShadowNodeHelper class
  • Support Frustum Culling for ArrayCamera in the Renderer -> Introduce FrustumArray
  • [] Support VSM Shadows in TileShadow

@sunag Feedbacks and help on the node part would be greatly appreciated!


This contribution is funded by Renaud Rohlinger @ Utsubo

Copy link

github-actions bot commented Mar 31, 2025

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 336.36
78.34
336.39
78.35
+31 B
+9 B
WebGPU 533.31
148.2
540.64
149.78
+7.33 kB
+1.58 kB
WebGPU Nodes 532.77
148.1
540.1
149.68
+7.33 kB
+1.58 kB

🌳 Bundle size after tree-shaking

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

Before After Diff
WebGL 465.37
112.21
465.37
112.21
+0 B
+0 B
WebGPU 604.89
164.17
613.53
165.84
+8.64 kB
+1.67 kB
WebGPU Nodes 559.88
153.6
568.52
155.25
+8.64 kB
+1.65 kB

@RenaudRohlinger RenaudRohlinger marked this pull request as ready for review March 31, 2025 16:24
@RenaudRohlinger RenaudRohlinger requested review from Mugen87 and sunag and removed request for Mugen87 March 31, 2025 16:24
@RenaudRohlinger
Copy link
Collaborator Author

@sunag I also added Frustum support for ArrayCamera via a new FrustumArray class. I’ve tested everything on both backends (Windows and macOS), and it all looks good so far.

I’ll move on to finishing VSM shadows in TileShadow, depending on your feedback, just want to avoid working on something that might get changed if you’re planning to work some magic on the TileShadow node side.

Would be awesome if you could take a look at the node when you get a chance! 🙇

@Spiri0
Copy link
Contributor

Spiri0 commented Mar 31, 2025

This would probably be a PR. Since this is about shadows, and I've been on the subject recently, I'm just asking.
Is it possible to use your shadowTexture in a custom shader?
I have to create my own light source to make this work satisfactorily, because with the standard directional light, the update has a frame delay, and the shadows flicker as a result when you move.

@sunag
Copy link
Collaborator

sunag commented Apr 1, 2025

It seems that the background with Cubemap without PMREM broke

@sunag
Copy link
Collaborator

sunag commented Apr 1, 2025

Would be awesome if you could take a look at the node when you get a chance! 🙇

I tried to help with some details, the PR is amazing, it will certainly be very useful <3

@RenaudRohlinger
Copy link
Collaborator Author

RenaudRohlinger commented Apr 1, 2025

Overall the PR should be good now.
Regarding VSM I checked but by definition VSM doesn't seem really compatible with the tile logic or it would be better to work on it in another PR if someone find somehow a compatibility.

I wanted to throw an error when using VSMShadowMap but I don't think I can do a check like:

const shadowMapType = builder.renderer.shadowMap.type;

if ( shadowMapType === VSMShadowMap ) {

	throw new Error( 'TileShadowNode: VSMShadowMap is not supported.' );

}

because the renderer is only accessible when the node gets build, not when instancing, resulting in AnalyticLightNode crashing while trying to read the custom shadow.shadowNode.

Also it feels too much like an edge case to pollute the code of AnalyticLightNode to fix this issue for such a small edge-case.

Would it be ok to just add a comment somewhere or ignore that case? /cc @sunag @Mugen87

@Mugen87
Copy link
Collaborator

Mugen87 commented Apr 1, 2025

I think you can mention the edge case in the JSDoc for TileShadowNode. That should make it clear.

@cmhhelgeson
Copy link
Contributor

Is the TileShadowNodeHelper automatically rendered outside of the postProcessing pipeline or does that have to be configured via MRT.

@RenaudRohlinger
Copy link
Collaborator Author

RenaudRohlinger commented Apr 2, 2025

The TileShadowNodeHelper is just a basic mesh in the scene, I just tricked the vertex shader to display it as a quad mesh, which means that it will be affected by the postProcessing pipeline. Also it's just a helper so I think it's fine.

@sunag sunag added this to the r176 milestone Apr 3, 2025
@sunag sunag merged commit 8cccca7 into mrdoob:dev Apr 3, 2025
12 checks passed
).mul( 1 / 17 );

} );
export const getShadowRenderObjectFunction = ( renderer, shadow, shadowType, useVelocity ) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RenaudRohlinger Would it be possible to refactor getShadowRenderObjectFunction() so it does not return a new function object on every invocation? updateShadow() is called per frame per shadow casting light so this might affect GC overhead.

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.

5 participants