Number of shadow casting lights

Zeross

Regular
For a project, a client is asking for something that seems impossible : high number of dynamic shadow casting lights in a relatively complex scene at an "interactive" framerate. Oh and of course without using top of the line GPU, it would be too easy :D

At first I dismissed this requirement, I was thinking that even the latest games were using maybe up to 5 shadow maps : one cascade for the sun and 4 spot lights, not several dozens. But then I became curious, were my figures accurate ? How many shadow casting lights is it possible to achieve not in a very contrieved demo but on a real 3D scene in a game for example. So I did some research, and I found some figures.

The latest 3DMark (FireStrike) for example is handling 100 shadow casting spotlights on DirectX 11 hardware (http://s3.amazonaws.com/futuremark-static/downloads/3DMark_Technical_Guide.pdf). Maybe more telling, Killzone 3 on the old PS3 hardware supports more than 10 shadow maps (http://www.dimension3.sk/downloads/valient12_shadows_in_games.pdf). So my intuition was on the low side, games are using more shadow casting lights that I thought...

My question is then : do you have some more examples ? If you're a developer, in your experience, how many shadow casting lights are you using ? I know this is a very rough estimate and it depends on a lot of things, but even some bounds would be useful. What are the best techniques to handle a large number of shadow casting lights ?

I found some useful resources like a chapter from OpenGL insights (great book by the way, if you're interested in OpenGL you should read it. There are not a lot of OpenGL books and this one is very bood) but I'm not convinced by what is proposed since it is using instanced geometry shaders. My gut feeling is that everything involving geometry shaders will be slow... The results are also not very telling, without a complex vertex shader the traditionnal (multipass) method is faster.

Any opinion on the topic ?
 
Forward+ maybe ? I had the feeling it can increase dramatically the number of light casting.
 
Last edited by a moderator:
Deferred shading and Forward+ are very interesting techniques and provide a good answer for handling a large number of lights but they do not provide an answer for managing a large numer of shadows.

Of course, I know that there is no silver bullet : if you want to render n shadow casting lights, you have to do n render to generate your shadow maps (potentially more with PSSM). Techniques like layered rendering only allow to decrease the overhead on the CPU by doing everything on the GPU, thus limiting the number of draw calls.

I guess the best technique is to tighten the light frustum and to be agressive on the culling to minimize the number of drawn objects in the shadow map.
 
Check out a presentation from Media Molecule regarding Little Big Planet 2. Their engine can support virtually dozens of real time lights, with shadows and volumetric shafts. It does so with a more exoteric aproach though, but maybe still worth considering depending of the kind of scene you will be dealing with in your project. LBP's aproach does voxelize the scene and stores the lighting into a Light volume, so all oclusion information is ray marched in voxel space rather than through conventional shadowmaps of today. Of course that only works because the whole game is confined to a 2.5D environment with very shallow Z depth (their voxel representation only had 16 Z values if I remember correctly) But yeah, it leads to some impressive lighting when it does work.
 
It's the same as anything else: if you want to do more, then you need to do less. In this particular case if you want to really scale up your number of shadow-casting lights, then you need to do as little work per-light as possible. Some guidelines:

- Don't do anything fancy when generating the shadow maps (variance shadow maps, geometry shader expansion, tessellation, etc.). Just render plain depth to a depth buffer (don't render to a color render target) with no pixel shader bound.

- Cull as aggressively as you can.
-- If a mesh isn't in the light's field of view, don't render it to the shadow map.
-- If a mesh doesn't cast a shadow on visible geometry (this is harder to check) then don't render it to the shadow map.
-- If a mesh is completely in shadow, then don't render it to the shadow map.
-- If a mesh casts a really small shadow relative to the screen, then consider not rendering it to the shadow map.
-- If a light doesn't intersect with the field of view, don't render it at all and skip the shadows.
-- If a light is completely occluded by geometry, skip it (can potentially use occlusion queries and predicated draws to do this)
-- If you're rendering 6 directions for a point light, skip any directions that aren't visible.
-- If no shadow-casting meshes intersect a light's field of view, turn off shadows for that light.
-- Consider not rendering shadows for lights that are far away.
-- If either your geometry or lights or static, precompute whatever you can.

- Consider casting shadows with simplified meshes. This isn't always something you can do since it can easily break self-shadowing, but it can work very well for certain cases.

- If meshes and lights are static, consider pre-culling backfacing triangles.

- If you're running on PC then number of draw calls can be an issue for CPU overhead, in which case it makes sense to use instancing if you can. You can also potentially pre-combine meshes to reduce draw counts.
 
It's the same as anything else: if you want to do more, then you need to do less. In this particular case if you want to really scale up your number of shadow-casting lights, then you need to do as little work per-light as possible. Some guidelines:


Thanks for these guidelines, I also wanted to thank you for your blog, it is a gold mine of informations and it was really helpful when I worked on deferred shading and cascaded shadow maps.
 
Shadow map caster numbers is a factor of two things, geometry rate and shader fill rate. The first obvious way to tackle geometry is very aggressive culling, its relatively simple work to go beyond a simple frustra vs sphere/box and can sometimes give you very good reductions. You can also look at portal and anti portal style tech to assist culling.
Fill rate you want to make sure the pixels your writing depth and testing against are useful, again clever culling and clipping helps as well as dynamic resolution of the maps themselves.

If you want to get exotic you can look into inversions, if your transforming geometry too many times you can render geometry once into a more convenient space and them do the shadow generation there. If the number of pixels each shadow caster hits is low then ray casting from the surface to the light source can be more efficient tho generally takes a lot of change to the rendering pipeline.

At the real extreme its possible to generate real time RLE fields to turn visibility queries into 2.5D line draws (or 2.5D DDA walks) which is very useful for extremely decoherent light sampling. I had a (slow) working example of that a year or so ago, but got distracted by something or other.
 
In Trials Evolution (Xbox 360, 60 fps game) we rendered up to 16 spot light shadow maps per frame (average around 5), and a PSSM sunlight shadow map (4 cascades, up to 2 kilometer view range). Because the game was always running at 60 fps, we didn't need to update all the shadow maps every frame. We did interleave the shadow map updates for light sources (and PSSM cascades) that were far away from the camera (update at 30 fps / every other frame).

We did use EVSM filtering for the shadow map (only two channels, not the negative ones). The shadow map blurring was done by EDRAM built in 4xMSAA, because that's free on Xbox 360. We didn't have any blurring beyond that (so the shadow edges were relatively sharp). The shadow map cascades had tight bounding boxes (we analyzed the depth buffer min/max depth values) in order to maximize the texel density, and to reduce number of draw calls to minimum. We also did radix sort (by CPU) all the objects rendered to shadow maps, in order to maximize the hi-Z culling performance. All our objects (and terrain) did cast real time shadows, because we couldn't bake any lighting offline (our game is based on user created content - all the levels are created inside the game). The lights were combined together using a tile based deferred rendering pass.

With DX11 (compute shaders + drawindirect + new tricks), you can render each shadow map with a single draw call. This will improve the performance a lot, since previously the high amount of draw calls were the most limiting factor of shadow mapping performance. In our Xbox 360 game, the shadow map rendering performed around 3x-4x more draw calls than the visible geometry pass. With new DX11 capable hardware, we just need a single draw (and a few compute shader dispatch calls) per shadow map. That's a huge gain.
 
Thanks to both of you for your insightful replies.

Sebbbi, up to 16 shadow casting lights per frame is impressive for a 60 fps game on a Xbox 360 ! The DirectX 11 technique that you've mentioned reminds me of the technique that is discussed in OpenGL insights using instanced geometry shaders but in this case the results were not as clear-cut. Did you have the chance to do some comparisons between single-pass DirectX 11 shadow maps and the standard multi-pass algorithm ? Did you observe a significant speed-up ?
 
Back
Top