Game Development Reference
To output the second-order SH-coecients for all three color channels, we
render this time into three 2D texture arrays with half floating-point precision.
Since all calculations are done entirely in voxel space and are limited to voxels,
which actually contain geometry information, this technique scales very well with
an increasing number of light sources of all different types.
In many situations we can even abandon the use of shadow maps for point
lights and spotlights without noticeably affecting the final render output. How-
ever, for large point lights, spotlights, and directional lights, we do need to use
shadow maps to avoid light leaking. Here we can simply reuse the shadow maps
that have already been created for the direct illumination step.
7.3.3 Propagate VPLs
In this step the previously created VPLs are propagated iteratively across the grid
according to the LPV technique proposed by [Kaplanyan and Dachsbacher 10].
Basically each VPL cell propagates its light along the three axes of a Cartesian
coordinate system to its surrounding six neighbor cells. While doing this propa-
gation, the voxel grid from the first step is used to determine how strongly the
light transport to the neighbor cells is occluded. The results from the first prop-
agation step are then used to perform a second propagation step. This is done
iteratively until we get a visually satisfying light distribution. In the first iter-
ation no occlusion is used, in order to initially let the light distribute; from the
second iteration on, we use the geometry occlusion in order to avoid light leaking.
The iterative propagation is performed in DirectX 11 by utilizing a compute
shader since we do not need the rasterization pipeline for this job. Listing 7.3
// compute shader
Texture2DArray inputRedSHTexture: register (t0);
Texture2DArray inputGreenSHTexture: register (t1);
Texture2DArray inputBlueSHTexture: register (t2);
StructuredBuffer <VOXEL> gridBuffer: register (t3);
RWTexture2DArray <float4> outputRedSHTexture: register (u0);
RWTexture2DArray <float4> outputGreenSHTexture: register (u1);
RWTexture2DArray <float4> outputBlueSHTexture: register (u2);
// directions to six neighbor cell centers
static float3 directions =
float3(0.0f,0.0f,1.0f), float3(1.0f,0.0f,0.0f), float3(0.0f,0.0f,-1.0f),
float3(-1.0f,0.0f,0.0f), float3(0.0f,1.0f,0.0f), float3(0.0f,-1.0f,0.0f)
// SH-coefficients for six faces (ClampedCosineSHCoeffs(directions[0-5])
static float4 faceCoeffs =