Implementing Voxel Cone Tracing

Original Author: Simon Yeung

[Updated on 25-2-2013: added a paragraph about lowering the voxel update frequency for faster dynamic update. The demo also added a combo box for choosing the update frequency]


In last year SIGGRAPH, Epic games presented their here which requires a DX11 GPU to run.

With GI
Without GI


There are 5 major steps in voxel cone tracing:

0. Given a scene with directly lighting only

1. Voxelize the triangle meshes

2. Construct sparse voxel octree

3. Inject direct lighting into the octree

4. Filter the direct lighting to generate mip-map

5. Sample the mip-mapped values by cone tracing

Step 0
Step 1
Step 2
Step 3
Step 4
Step 5

The step 1 and 2 can be done only once for static geometry while step 3 to 5 need to be done every frame. The following sections will briefly describe the above steps, you may want to take a look at the original paper first as some of the details will not be repeated in the following sections.

Voxelization pass

The first step is to voxelize the scene. I first output all the voxels from triangle meshes into a big voxel fragment queue buffer. Each voxel fragment use 16 bytes:

The first 4 bytes store the position of that voxel inside the voxel volume which is at most 512, so 4 bytes is enough to store the XYZ coordinates.

Voxel fragments are created using the conservative rasterization described in this shader generator to generate the shaders for creating voxel fragments based on the material.

Scene before voxelize
Scene after voxelize

Octree building pass

I use a similar data structure that described in the giga voxel paper with a large octree node buffer storing the octree/voxel node data (with 8 node grouped into 1 tile which described in the paper) and a 3D texture storing the reflected radiance and alpha from the direct lighting at that voxel (I assume all the surface reflect light diffusely so only 1 3D texture is used which is different from the paper storing incoming radiance).

Each octree node is 28 bytes with the first 4 bytes storing the child tile index with another 24 bytes storing the neighbor node byte offset from the start of the octree node buffer: