Shadow map is now widely used in realtime shadow rendering. The basic concept of shadow map is very intuitive. We treat the light as a camera. Watch from the light and all the invisible areas are shadows.
With this basic concept, shadow rendering always involves two passes. In the first pass, we align the camera with the light, and then render the whole scene with depth information. This is the shadow map. During lighting, we convert the pixel position to the light(shadow camera) space, then we compare the depth value the with corresponding pixel in the shadow map we rendered before. If the current pixel is further from the light, then it’s in the shadow. Otherwise it’s lit. Easy to understand and easy to implement.
Directional light’s shadow
When we build the shadow camera, we need different parameters for different types of lights. Directional light like the sunlight is considered infinite far from us. So it’s treated as a light that emits parallel light rays. So the shadow camera for directional lights is an orthogonal camera. It only has a size that can encapsulate the lights range or view camera frustum if the light is considered infinite.
Punctual light’s shadow
The most commonly used punctual light model is spot light and point light. A spot light is a cone shape light. It has an angel parameter to describe how wide it the light shape. So a square ratio perspective camera is used to render spot light shadow map. The FOV of the camera is corresponding to the angel of the light.
But a point light is a sphere shape. It’s impossible to represent a sphere with a single camera. So the most commonly used way is represent the sphere as a cube map and use six 90 degree camera to render each face of the cube map. Then during rendering, we decide which face is current pixel in, compare the depth and decide the attenuation.
Cascade shadow map
When we use a perspective camera, the pixel density of a unit world space area is not identical. Because of perspective projection, near fields of camera produce more pixels. If we use one shadow map for the whole camera frustum, it will result in either oversampling at near areas or undersampling at further areas. And because camera has a frustum shape, a lot of areas in the square shaped shadow map will be wasted.
So we split the camera frustum into several parts from near to far. Then, we use bounding volume for each part of them to render shadow map instead of using a bounding volume for the whole camera. With this approach, we have a tighter bound volume and more appropriate sampling rate for each part.
How should we split the frustum? The Parallel-Split Shadow Maps on Programmable GPUs shows us that we can lerp uniform splitting and exponential splitting based on how our camera is positioned. For example, a top-down camera can use uniform splitting, and a look-at-horizon camera which can see super far should use split more close to the exponential distribution.
Cascade shadow map is not perfect. It still has several problems:
- Though fewer areas are wasted comparing to a single shadow map, there are some repeated draws in the overlapping area between neighbor cascades;
- Cascade size varies a lot, especially when using exponential splitting. With the same shadow map size, there may be obvious shadow quality switch at the edge between cascades.
The first issue is unavoidable because we need to confirm that the shadow camera covers the cascade frustum completely to avoid unshadowed seam between cascades.
For the second problem, the common way is to do a blend at the edge of two cascades. It solves the edge problem with a cost of evaluating shadow twice at the blending area. This is a quality-performance trade off.
Stable cascade shadow map
Another problem with directional lights’ shadows is that the shadow camera always moves with our camera. And for performance reasons, under most circumstances, our shadow map is under sampled. When we rotate our camera around, light space AABB box size that corresponds to the ortho camera size changes in world space. With the shadow map size remains the same, the shadow map sampling density changes. On the other hand, when we translate the camera, a world space point’s rasterization state changes discretely in shadow map space. So when we move continuously in world space, the shadow appears to be flickering or shimmering.
For the first problem, we change the size of the ortho shadow camera from a light space AABB box to a bounding sphere. When using a sphere, the shadow camera size remains constantly when we rotating our camera like the image below.
For the second problem, we along the camera range in pixel size level with the actual shadow map. After we calculate new shadow camera position and size after the main camera moves, we re-adjust the camera position and size according to the shadow map pixel size and resolution. This will make the camera move by the integer times of pixel size and ensure that the same point in world space always refers to the same depth value in shadow map.
By comparing shading pixel light depth and the corresponding shadow map depth, we can know that if the current pixel is in shadow. This is the basic concept of the shadow map. With one sample in shadow map for each pixel, the shadow value is either 0 or 1. This will result in a very sharp shadow. Especially when the shadow map is under-sampled, the shadow will not only sharp but also aliased. So soft shadow is needed.
The most traditional shadow map is Percentage Closer Filtering (PCF). The basic idea of PCF is like all the blur methods. We sample the shadow around the light space position, and do the convolution of all the shadow results with a kernel. This will results in a blurry shadow attenuation at the edge of the shadow area. The larger the kernel, the softer the show.
Another real-world shadow feature is that the further the shadow caster to the shadow receiver, the softer the shadow. So here comes the PCSS which use a kernel size according to the shadow distance between caster and receiver. The further the distance, the larger the kernel size. This is pretty expensive, but the result is very pleasing.