Add Support for Static Shadow Optimization in Babylon ShadowGenerator

When adding a mesh to the list of shadow casters for a ShadowGenerator, it would be incredibly useful to flag meshes that do not require updates when updating the shadow map.

A practical scenario would be an open-world scene, where numerous static meshes would not need to update their shadows.

Would this be difficult to implement for the Babylon ShadowGenerator?

If possible, I would be willing to pay a bounty for someone skilled to implement this feature. :slight_smile:

2 Likes

chatGPT:
It is definitely possible to optimize the shadow mapping process by flagging meshes that do not require updates when updating the shadow map in Babylon.js.

One way to achieve this is by adding a custom property to each mesh, such as “isStatic” or “doesNotCastShadows”, and then using this property to filter out meshes that do not need to be updated when updating the shadow map. For example, you can use the mesh.isVisible property to check if a mesh is currently visible in the scene, and skip updating its shadow if it is not.

Here’s an example implementation using the shadowGenerator.getShadowMap().renderList property:

// Add all meshes that cast shadows to the render list
shadowGenerator.getShadowMap().renderList = scene.meshes.filter(mesh => mesh.receiveShadows);

// Filter out static meshes that don't need to update their shadows
shadowGenerator.getShadowMap().renderList = shadowGenerator.getShadowMap().renderList.filter(mesh => !mesh.isStatic || mesh.isVisible);

In this example, we first add all meshes that cast shadows to the render list, and then filter out any meshes that have the isStatic property set to true and are currently not visible.

Implementing this feature should not be difficult in Babylon.js, and it can significantly improve the performance of shadow mapping in large scenes with many static meshes.

answer modified and verified:
shadowGenerator isStatic mesh | Babylon.js Playground (babylonjs-playground.com)

well what do you consider static? Even if the mesh never moves , if you move the camera the shadows need to update. Anything else is basically in the realms of baking the shadows.

Yes, you are right, shaderbytes. The shadow of the “static” meshes should be buffered in any way. The solution of chatGPT and my modification is garbage. I should have thought more deeply about it before publishing. Thank you pointing it out.

1 Like

We don’t want to filter meshes from the shadowMap’s render list. Instead, I’m looking for a solution to add shadow casters to a shadowGenerator and flag meshes that don’t need to update their shadows in order to improve performance.

I am unsure if this conflicts with the nature of how shadow maps are generated.

2 Likes

It can only work if all shadow generator meshes are static. In that case, you only need to set the refresh frequency to 0 so that the shadow map is not regenerated each frame. But you can’t have static + non static shadow generator meshes in a shadow generator render list.

… or you generate your own shadows. This is a simple version which considers the shadow on a flat plane, but if you can control things on your own you can do anything with!

shadow generator | Babylon.js Playground (babylonjs-playground.com)

even volumetric shadowing:
shadow generator | Babylon.js Playground (babylonjs-playground.com)

So, at this point would you say that having two generators would eventually do the trick. One for dynamic and one for ‘static’ shadows?

Honestly, generating own shadows seems a bit too fancy to use in production mode. To me it looks more like a dev demo (a nice trick though) but quite inappropriate to use at a larger scale.

Yes, but at the expense of GPU memory consumption. Also, it could be hard to setup because you would need two lights, which will add up when lighting the scene.

The best way would be to bake shadows for static meshes, as said by @shaderbytes.

Yes, I understand. In fact, I did this before in one of my test scenes. Problem with baking shadows is I don’t think @webgrid can really do that. I guess you know his game (at least a little). I don’t see how he could easily bake shadows within his map generator. It would by any means require a lot of work.

I experimented with two shadow generators, one dynamic and one static, as you mentioned and that worked, but it consumed may a little much CPU. I cannot bake the shadows since my landscape can change and is semi-procedural.

My solution involved using performance “friendly settings” for the shadow generator and removing small 3D objects that wouldn’t be that noticeable to the player.

My next step may be to improve the segmentation of the landscape so that I can further reduce the render list.

Shadows are soo cool, but consumes too much resources :slightly_smiling_face:

Thanks for helpful feedback!

Don’t remember where I found this static shadowing of a mesh, but this is also worth to mention if your light sources are always on the same place, since the shadows will not change their positions in this case!

Lightmap test | Babylon.js Playground (babylonjs.com)

I love the idea of mixing static and dynamic meshes in shadows !!!

As long as the light and some meshes are static related to the light, we could store them in a texture and copy it to bootstrap the shadow pass every other frames.

I wonder how hard it could be to implement but an amazing perf trick.

That would be a nice improvement, but you’d also have to bootstrap the zbuffer. I don’t know if it’s easy to do in WebGL (in WebGPU, the zbuffer is a normal texture, so we should be able to read/write to it, even if I think there are restrictions on the operations we can do with a zbuffer texture)…

It would be amazing!

We all know that working with lightmaps is a tedious process.

Some time ago I saw a demo of Progressive Lightmapping in ThreeJS that caught my attention.

https://threejs.org/examples/webgl_shadowmap_progressive

Yup I guess the first render would be filling it and then we rely on copy copyTexImage2D to dupplicate ?

Yes, if copyTexImage2D works with a depth texture (I know that for the fluid renderer, I use a render pass to copy the texture - the CopyTextureToTexture helper class).