Rendering Shadows in Real-Time Applications 1: Static Shadows
Rendering shadows on Real Time Applications presents distinct challenges compared to their offline counterparts. On Offline ray/path-traced applications, shadows come “for free” as part of the rendering algorithm but for real time we normally need to rely on physically inaccurate, multi-pass techniques.
On this series of articles, I’ll explain different ways we can achieve this.
I would classify the most common techniques in the following way, depending on use case and type of light:
- Static
- Baked Light Maps - Dynamic
- Shadow Maps
- Stencil Shadows
In any case, all these techniques involve generating one or multiple textures with the shadow information that will be sampled in the lighting pass to determine if a fragment is in shadow or not.
Let’s start with static shadows.
Baked Light Maps
If we know for sure that the light sources and the shadow-casting objects are not going to change, we can use the power and simplicity of our offline cousins and create a baked light map.
This would have the extra benefit of providing high quality lighting information as well, although we could bake just the shadows if we want to.
To bake a light map we ray/path trace the scene from each light, adding the contribution of each ray to one or many textures that map every static object of the scene, and will be stored on disk after we’re finished.
Later, when we render the scene, for each static object, we simply sample the texture(s).
This of course would require us to organise the scene into a hierarchy that allows not only the ray/path tracing algorithm to work, but also to map each static surface to a unique region of the texture, and in a way that the texture coordinates can be retrieved during rendering.
This technique will provide the highest quality and most physically correct shadows as we’re simulating the light rays bounces around the scene, but won’t be applied to moving or changing objects, which can create visually incoherent issues if not handled properly.
This technique is also the cheapest at runtime, since it only requires sampling a single texture.
It could also be combined with other real time techniques to be able to use dynamic light sources such as flashlights or vehicle headlights, or to add shadows for moving objects.
I’d choose this technique if:
- The environment and main shadow-casting light sources are static.
- You want the highest quality and most realistic shadows possible.
- You want to reduce the number of real-time shadow-casting light sources while keeping the shadows they cast on the environment.
- And/Or you want a high number of shadow-casting lights
An example of an ideal game for this technique would be a photo-realistic first person walking simulator in a space ship.
I’d avoid it if:
- You have a changing environment:
- Trees moving with the wind
- Destructible structures
- User-built structures - The shadow-casting light sources change color, position or intensity over time:
- Day-night cycle
- Flickering or switchable lights
An example of a worst case game for this technique would be a cartoonish real time strategy game with a day-night cycle.
📚 References
1. “Lightmapping: Getting Started” (Unity docs) https://docs.unity3d.com/2018.2/Documentation/Manual/Lightmapping.html
2. Battlefield 4 example: “What is Baked Lighting? (Lightmap)”: https://www.youtube.com/watch?v=eGmXDOGjKPA