Recently, over the summer, I developed a dynamic shadow mapping system for real-time rendering. After finish expanding on my Lighting System, something I noticed was desperately missing from it was shadows. They sounded simple enough, just render the scene from the perspective of the light and BINGO-BANGO shadows!!
Unfortunately it would not be so easy… in the end however, I am proud of what came out of it. The system supports multiple light types (directional, point, and spot lights) with configurable shadow map resolutions and flexible shadow-casting options. The system efficiently manages shadow resources, minimizes data transfer to shaders, and dynamically adjusts resource usage based on the number of active lights.
Key Features:
- Shadow Support for Multiple Light Types:
- Directional Lights: Shadow maps are generated using a standard projection matrix, capturing shadows across large areas.
- Spot Lights: Shadow maps are calculated using a perspective projection matrix for focused areas.
- Point Lights: Shadow maps are implemented using
TextureCubeArray, allowing shadows to be rendered in all directions from the light’s origin. This is achieved by rendering the scene from six different perspectives, each corresponding to a face of the cube map.
- Efficient Use of
TextureCubeArrayfor Point Lights:- For point light shadow maps, a
TextureCubeArrayis utilized, with each cube containing six faces (one for each direction: +X, -X, +Y, -Y, +Z, -Z). Each cube map is dedicated to a single point light, ensuring accurate omnidirectional shadow rendering. - The system dynamically creates and manages shadow resources, including depth-stencil views (DSVs) for each face of the cube maps and shader resource views (SRVs) for sampling the shadow maps in shaders.
- For point light shadow maps, a
- Dynamic Resource Allocation and Constant Buffer Optimization:
- The system intelligently scales the amount of shadow data sent to the shaders based on the number of active shadow-casting lights. This is achieved by grouping lights into small, medium, and large shadow configurations, minimizing memory usage and avoiding sending excessive data to the GPU.
- Cube Map Rendering for Point Light Shadows:
- For each point light, the scene is rendered from six different perspectives (corresponding to each face of the cube map) using custom view matrices. A single projection matrix is shared for all faces, and the results are stored in the
TextureCubeArray. - This technique allows for accurate omnidirectional shadow casting from point lights, crucial for realistic lighting in complex scenes.
- For each point light, the scene is rendered from six different perspectives (corresponding to each face of the cube map) using custom view matrices. A single projection matrix is shared for all faces, and the results are stored in the
- High-Performance Rendering Pipeline:
- The system efficiently manages shadow rendering by minimizing the number of buffer updates and resource bindings. Dynamic constant buffers are used to update only the necessary data per frame, reducing the amount of data transferred between the CPU and GPU.
- Advanced Sampling in Shaders:
- In the shaders, shadow maps are sampled using percentage-closer filtering (PCF) for soft shadows. For point lights, the system calculates the appropriate cube map face to sample from based on the light’s direction relative to the object.
- Shadow maps are accessed using
TextureCubeArrayfor point lights, andTexture2Darrays for spot and directional lights, allowing for efficient shader execution and minimal branching.
Challenges and Solutions:
- Efficient Resource Management: To avoid overloading the GPU with massive constant buffers, the system dynamically adjusts the number of shadow maps and shadow-casting lights in use. This allows for scaling across different hardware capabilities and scene complexity.
- Optimized Shader Logic: To prevent the shaders from being bloated with shadow map calculations for inactive lights, the system only sends active shadow data and reduces unnecessary computation.
All the work I’ve done so far has been on my rendering engine, jAzul and I have yet to integrate it into my actual game engine, MOABE, let alone make a game utilizing this new capability, but… baby steps.
