In the real world, all objects are lit by a combination of direct light (photons that travel directly from a light source to illuminate an object) and indirect light (photons that travel from the light source, hit one object and bounce off of it and then hit a second object, thus indirectly illuminating that object). (NVIDIA) “Global illumination” (GI) is a term for lighting systems that model this effect. Without indirect lighting, scenes can look harsh and artificial. However, while light received directly is fairly simple to compute, indirect lighting computations are highly complex and computationally heavy.
Voxel Global Illumination (VXGI) is a stunning advancement, delivering incredibly realistic lighting, shading and reflections to next-generation games and game engines.
VXGI is a software library for a complex rendering technique. This algorithm uses Voxel Cone Tracing to accumulate light contributions in the scene, including indirect light.
This can not be build into the graphics driver, so we cannot switch the graphical properties if we want to use it in a game, for example. It needs to be integrated into the game engine, but it is hard to do it. The Unreal Engine 4 is one of the few implementations available.
This technique differs on previous alternatives because it uses a different data structure to encode the voxel data. Instead of using a Sparse Voxel Octree (like the Sparse Voxel Octree Global Illumination (SVOGI)) , it uses a 3D Clipmap.
A Clipmap is a hierarchical data structure similar to a 3D mipmap with the difference of clipping finer levels, in order to not exceed a pre-defined size.
The algorithm encodes the opacity and emittance information in the climaps and the executes the Voxel Cone Tracing to compute approximately the global illumination in real time.
In the slider bellow you can observe an easy way to distinct VXGI from direct illumination. This slider was taken from NVIDIA.
Demonstration of VXGI vs Direct Illumination by NVIDIA.
The main steps of the VXGI technique consists on the voxelization of the scene, encoding the opacity and emittance information in the clipmap and then voxel cone tracing is used to compute global illumination.
For that purpose each level needs to be clipped to a user-specified maximum size (ClipSize). This parameterization results in an obelisk shape for clipmaps as opposed to the pyramid of mipmaps. It also defines the size of the texture memory cache needed to fully represent the texture hierarchy.
ClipSize represents the limit, in texels, for any single level of a clipmap texture. In a normal mipmap, the texture size in memory would be bigger. Instead, the Clipmap retain the logical size and render-time accessing the corresponding level of a full mipmap.
Further is defined the
Below the Clipmap Stack is the Clipmap Pyramid, defined as the set of levels of sizes lower than the ClipSize limit. These levels are completely stored in texture memory and have the same spatial resolution of a full mipmap.
The regions near the camera are represented with finer levels of detail and far regions with coarser levels of detail. This maps nicely when applying voxel cone tracing, due to the higher spatial density of pixels near the camera (Daniel Gomes et al, 2015).
To prove the Clipmap storage efficiency lets consider a 9 level 2563 Clipmap to be rendered on a 323 display. Given the display size, we know that the upper bound on texture usage from a single level is 643, so we must set the Clipsize to 64. There will be one clipped level forming our Clipmap Stack and 8 levels in the Clipmap Pyramid. The storage required for this is 643 texels * 2 levels + 8/3 * 643 for the pyramid = 2.33 MB at 2 bytes per texel. This clipmap configuration requires only 2.33 MB of the full 85.3 MB of a full mipmap. The bigger the size of the clipmap, the bigger is the difference of memory usage between the mipmap and Clipmap. This is essencial to store the textures on the graphics dedicated memory and avoid constant swaps with RAM. The table below shows the differences.
|Type and Size||163||323||643||1283||2563|
|643 Clipmap||1.8MB||2.33MB||1283 Clipmap||14.7MB|
The clipmap must however be updated when the camera moves in order to maintain all the information needed around the camera to render the current frame. In order to prevent having to update the clipmap completely each time the camera moves, it is possible to take advantage of the fact that usually only a small part of the clipmap needs to be updated (a great portion of the data is already present in the Clipmap since it was needed in the previous frame) (Daniel Gomes et al, 2015). In order to update the Clipmap incrementally with the needed data, toroidal addressing is used (Tanner et al., 1998). Toroidal addressing is a method that guarantees that a point in space always maps to the same point in the texture as shown in the next image.
The first step of the VXGI algorithm is the opacity voxelization and downsampling (NVIDIA VXGI). In this step, the scene geometry is converted to a map which encodes the opacity of space with voxels.
The voxelization process starts by choosing the projection plane in order to maximize the number of fragments generated during rasterization (Daniel Gomes et al, 2015). However, the rasterization is performed in Multisample Anti Aliasing mode (MSAA) in order to produce multiple samples for the rasterized triangles.
Then the depth coordinate is computed for each sample and reprojected into the other two planes.
Finally, a bit count function is used in order to convert the coverage masks obtained by MSAA to an opacity value.
In this pass the opacity values calculated before are used to determine the amount of light that geometry in their volume reflects or emits in all directions.
Starts by selecting the projection plane and rasterize the triangle. After that, the approximate light intensity for each voxel is computed. It can project the intensity to 3 or 6 directions. Finally, the directional intensities are accumulated for all rasterized triangles.
Objects can change their brightness abruptly if they move, due to alterations to te number of covered samples(small objects or large voxels in the coarse clipmap levels) (NVIDIA). To solve this problem, supersampling is used.
The coverage masks are then used to generate the brightness value of the fragment and the emittance is computed with the help of a shadow map generated previously.
The final step is the cone tracing. This is the process that computes approximatly the surface irradiance coming from a set of directions, given a primary direction and the cone angle. This technique is described by the equation below.
The cone traverse the given direction, taking samples from the emittance texture that contribute to irradiance. In order to reduce light leaking, occlusion is also calculated as the product of opacities from all the previous samples (NVIDIA).
This step computes the diffuse and specular indirect illumination. The diffuse tracing shoots several cones from every visible pixel, into a set of directions covering the hemisphere.
Specular tracing shoots one cone from every visible specular surface in the reflected view direction. If the surface is rough, it is used a wider cone.
For shinier surfaces, a thiner cone is used. Due to the low spatial resolution, it is impossible to compute mirror-like reflections similar to ray tracing algorithms.
This section has the purpose of helping the users to test the improved demo that we made based on NVIDIA's demo. It contains a detailed tutorial to ease the interation between the user and the interface.
If you want to try the demo for yourself you can download it here.
We only provided the Sponza model in our link above, but you can download other famous models like Rungholt and Power Plant here.
After starting the application, the object is shown with the respectively frames per second. A simple interface is also displayed in the upper-left corner with modifiable parameters that change the scene appearence in real time.
To view the voxelization process you can alternate the debug options by pressing 'g'. As you can see below, the scene is first voxelized and after that is performed the emittance voxelization to emit and reflect the light injected directly in the scene.
|Main Scene||Opacity Voxelization||Emittance Voxelization|
Also is possible to switch on and off the global illumination of the scene by clicking on the Enable GI button. The results are shown on the images below.
|No Global Illumination||With Global Illumination|
The objects that are not directly illuminated do not have any color. They are only visible due to the ambient color. Disabling this parameter(Ambient Scale value is 0.0) the non-illuminated sections are black. By enabling the global illumination are now visible due to the indirect diffuse color and specular light.
|No Global Illumination||With Global Illumination|
The scene may suffer from banding issues. To reduce this effect we can use the Cone Rotation parameter. It performs a random per-pixel rotation of the diffuse cone set. This results on a better visual result, but costs some performance.
|No Cone Rotation||With Cone Rotation|
By enabling and disabling the shading parameters, like the diffuse color or specularity, we can see different results on the desired scene.
|Only Direct Light||Ambient Color Only||Diffuse Color Only|
|Specular Light Only||All parameters Enabled|