Making Everything Darker : Real-Time Shadows
Shadows are one of the key features for creating realistic and believable virtual scenes. Whether hard or soft or physically correct, shadows are very crucial in providing important visual cues. Shadows can be an important aspect of gameplay also. For example, it plays an important role in CounterStrike by allowing us to see enemies around corners, coming through doors, etc.
Real-Time Shadows is an active area of research. There’s still no single fool-proof technique of rendering shadows that can handle all the cases. Well, there exists a way to render high quality physically correct shadows through ray-tracing but that’s not fast enough to render in real-time on current hardware. So a better way to say this is that there’s no real-time hack of rendering high quality, flicker/ jaggies free, physically correct, etc shadows yet.
My Goal is to render shadows in real-time without any pre-processing.
There are a lot of different techniques to render shadows in real time each having its own pros n cons. I am using one of the most popular technique of Shadow Mapping. There are many many variations or ways of implementing Shadow Maps too, I am using single depth map based shadow maps together with PCF, Poisson, random disk rotated Poisson, etc for filtering.
I am using depth only pass to update shadow maps (using pixel shader only for alpha test geometry) and Geometry shaders are used whenever I have to update multiple shadow maps whether for point lights, spot light batches or CSM.
I have shadows for all 3 types of lights supported in the engine –
Spotlights are assigned single shadow map per light. If we have more than 1 dynamic spotlight active, I batch the shadow map update of multiple lights together using Geometry shader. We can also configure max number of lights in one batch using a simple static variable MAX_NUM_SPOTL_PER_UPDATE and everything will be configured automatically. We can also configure the max number of dynamic spotlight shadows and/or static shadows that can be used, all using simple static variables and everything like constant buffers, texture allocation, etc will be configured accordingly. Only one batch of spot lights is updated per frame due to performance reasons.
Point Lights use 6 textures per light. TextureArray Depth View is used while rendering to shadow maps and TextureCubeArray is used while rendering. Only 1 point light is updated per frame. And we can have both static point light shadows (which will be updated only once) or dynamic shadows.
For directional light, I am using Cascaded Shadow Maps with 3 or 4 dynamic cascades and a static map (usually 2x the size of cascade map) which is used beyond MAX_DYNAMIC_SHADOW_DISTANCE. All cascades are updated at once using Geometry Shader Slicing. Red, Green, and Blue represent cascade levels 0,1 and 2.
I will be posting more stuff like performance related numbers and an overview of filtering / fixes I have used to make shadows stable (for both CSM and local lights).
Posted on February 3, 2015, in Game Development, Graphics, JustAnotherGameEngine, Shadows and tagged Cascaded Shadow Maps, CSM, Graphics, JustAnotherGameEngine, RealTime Shadows, Shadows. Bookmark the permalink. Leave a comment.