Why do shadows use so many indices?

I’m trying out CascadedShadowGenerator, which works fine, but I’m amazed about the number of indices that is added to the scene, when I use it.

https://playground.babylonjs.com/#X84Y3N#1

In this example I used only a basic box, so the scene has only 42 active indices without shadow, but 186 when the shadow is enabled! And 62 faces instead of 14.

This may not seem like a lot, but when I use a few bit more complex meshes (of around 700 indices each), the number of indices and faces for the shadows add up a lot.

Could anyone explain why a simple shadow of a box on a flat plane needs so many indices?

That’s because, by default, CSM renders the scene 4 times in 4 different textures (cascades). You can lower the number of cascades through the numCascades property for better perf but lower quality. It’s a trade-off, as always.

Ok, I see. But does this mean that a scene with shadows enabled is on average 4-5 times slower (so can handle 4-5 times less objects) ? Or are the shadow-meshes not very expensive, compared to other meshes that have textures, normalmaps, etc ?

I’m working on a game with a forest that has about 100 visible trees at a time (of each 750 vertices). I was a bit shocked to see that with shadows enabled, together with my main character this already leads to about 800.000 active indices, and I still have to add other objects as well. On a 2010 low-end laptop this results in a FPS lower than 10, which worries me a bit.

I looked at LOD and simplify, but this doesn’t really work well with the trees I have, so I’ll have to create low-poly meshes of the tree for the distant trees. I also cannot use camera.maxZ, because this makes the skybox invisible. Is there any way to remove the skybox from the frustum culling?

I also found a strange thing happening. The number of active indices doesn’t go down at all when I move the camera to a position with no trees at all in sight. But with the shadowgenerator disabled, the number of active indices is very low when I move the camera out of sight, as if no trees were ever present in the scene. What causes the active indices to be the same number, even though no shadows of trees need to be shown?

Thanks for your help, trying out 3D programming for the first time, but there’s still a lot to learn :slight_smile:

In case your lights do not move so much, consider bake the shadow into textures, it could use more memory/storage, but would have better rendering performance

1 Like

I’m using a heightmap to create the ground. And I’m planning to use some kind of tiled map, consisting of different tiles, each having a different heightmap and objects can be placed anywhere on these tiles.
I think that baked textures would not be practical in this situation?

Update:
I get much better FPS when in the renderloop each tree is checked if it is in the frustum of the camera or not. I also check the distance to the camera, to hide trees that are far away in the back.

Something like this:


        const maxdistance = 400;
        for (let t = 0; t < trees.length; t++) {
          if (
            !camera.isInFrustum(trees[t]) ||
            BABYLON.Vector3.Distance(
              camera.position,
              trees[t].position,
            ) > maxdistance
          ) {
            trees[t].isVisible = false;
          } else {
            trees[t].isVisible = true;
          }
        }

No, because the shadow map generation is only a part of the work done to render a scene. It’s hard to give figures, you should time your scene without CSM, then with CSM + num cascades = 2,3,4.

You can remove any mesh from the frustum culling test by doing mesh.alwaysSelectAsActiveMesh = true.

That’s because, by default, CSM does not implement culling per cascade. You can do it yourself, though, see:

https://doc.babylonjs.com/features/featuresDeepDive/lights/shadows_csm#culling

Thanks for your reply!

skybox.alwaysSelectAsActiveMesh = true; doesn’t work with camera.maxZ = 300 though!
However while searching for your method online I found:
skybox.ignoreCameraMaxZ = true;, which does the job!

Do you think the getCustomRenderList way of culling is faster than my method (isInFrustum)? It seems a lot more complicated (and thus slower?) with getCascadeViewMatrix(layer), getCSMTransformMatrix(layer), getCascadeMinExtents(layer), etc.

Any chance that culling will be optional in the future for meshes with a shadow as well?

It’s not the same culling. What you do is camera frustum culling, while getCustomRenderList is for cascade culling from the light point of view. If camera frustum culling is enough for you, then you can avoid light culling. Light culling does indeed take some time, so whether it’s an improvement or not for you will depend on your scene.

Not sure what you mean?

Well, just because you said: ‘That’s because, by default, CSM does not implement culling per cascade. You can do it yourself, though’ , I misunderstood ‘by default’ as ‘not yet’ :slight_smile:

For now, the camera frustum culling seems good enough. Thanks again, it’s an amazing engine. Hope to be able to show the game someday :slight_smile:

3 Likes