Voxel-based ambient occlusion using sparse textures

OpenGL ARB_sparse_texture extension allows creation of sparse textures, which only occupy GPU memory for ‘portions’ (or pages) of their declared sizes. This partial occupation reduces memory consumption for textures that don’t require full storage. One such application is representing a scene using voxels. Normally a large part of a 3D space enclosing a scene is empty space, so most of the texels are wasted if a normal 3D texture is employed to represent a scene. Sparse Voxel Octrees (SVOs) exploit the sparsity and store the voxels representing a scene in a compact form. And since a SVO is essentially an octree, a hierarchical data structure, sampling the scene information with mipmap filtering is achievable.

Sparse textures offer similar data compaction, and mipmap filtering is supported natively by hardware texture sampler. And the usage and implementation are also less engaging than those of SVOs. The drawback is that we need hardware supporting ARB_sparse_texture extension, and for advanced functionalities, such as querying texture page residency information inside a shader, to be available, support of EXT_sparse_texture2 extension is required, currently only nVidia’s 2nd generation Maxwell architecture supports this extension.

Support of ARB_sparse_texture extension in my nVidia GTX670 enables me to implement voxel-based global illumination using sparse textures. The technique

The following is a snapshot of a voxelized scene stored in sparse texture.
Without EXT_sparse_texture2 support, the texel value returned from texel fetching is undefined if the requested texel is not in memory. I think it’s safe to assume the value is 0 in nVidia’s implementation; the black outlines is the result of blending between non-empty and empty voxels.

Ambient Occlusion is a natural extension from the GI implementation. For each pixel several cones are shot to test the occlusion along the cone directions.



And the following is a debug view from my program, showing cone directions in each vertex:


The occlusion test is done by checking if there is non-empty voxel along the cone directions. As an AO cone progresses outward it get wider and the corresponding occlusion test  should check larger voxels (voxels covering larger space). This could be done by testing different mipmap levels of the sparse texture.

Here is a snapshot from my current work:


4 thoughts on “Voxel-based ambient occlusion using sparse textures

  1. Hi,

    i’m currently implementing something similar. I have some question i hope you can answer.

    – How does the OpenGl “generateMipmap” behaves for partial non resident regions?
    For example: you have a 4x4x4 texture with 3 non resident regions to be mipmapped into the 2x2x2 next mipmap level.

    – How do you sample with the cone in the correct mipmap level? How does your fallback in to a resident LOD/MipMap level works? Do you use a Min-LOD Table?
    For Example: your cone aperture at distance ‘d’ requires to sample the voxel volume at position ‘p’ at level 5 but level 5 isn’t resident and the next resident mip map level at ‘p’ is 8. How do assure that you sample from the correct LOD level?

    Thanks alot for your time.



    • Hi sorry for the late reply, I just noticed your comment @@

      To find out the resident pages in a level, I check if a page’s 8 child pages in lower level are all empty (zero values)
      I did it using compute shader, and write the indices of resident pages into a 1D buffer, which is then read back to CPU.
      After committing the resident pages, I used another compute shader to filter the attributes in lower level notes into these resident pages.

      About the cone tracing, I am not sure why you need to check multiple mipmap levels for a particular cone width.

      My cone tracing approach was that I first determine the cone width, that is, mipmap level, I want to sample based on the current ray offset ‘d’, and then I just sample that particular mipmap level.
      The attribute of a voxel in level n is the filtered values of its 8 children in level (n-1). I think if a cone width is the dim of a voxel in level n, then sampling level n is enough.

      Because my video card only has sparse_texture 1 extension, I can’t check the residency of a texel inside shader.
      I assume NV’s implementation of texture3D simply returns 0 if the fetched texel is non-resident. (It appears that way in my test)
      So the tracing algorithm is the same for a sparse 3D texture as for a normal 3D texture.

  2. Thanks for this awesome post. One of my problems in rendering ambient occlusion is GPU memory for my GPU accelerator isn’t that high-end. Sparse textures will help me produce AO’s for my objects faster than before. I’m still new to ambient occlusion and researching for more techniques. This post is helpful. Thanks!

  3. Hello, how did you manage to write to the sparse volume texture?
    EXT_sparse_texture2 sadly only defines reading operations. So imageStore isn’t “commiting” at least on my side. Without writing operations on the shader I honestly don’t see the point :(.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s