Implementing Vulkan Path Tracing in Hazel Engine

Path tracing, a powerful rendering technique, offers simplicity with its almost implicit features like soft shadows, global illumination, reflections and refractions. Unlike rasterization, which naturally lacks global illumination, path tracing creates more realistic and visually pleasing images by simulating the way light bounces around in a scene.

Integrating Vulkan path tracing into the Hazel Engine was a challenging task that took me over three months to complete. I had to make major structural changes in the engine, ranging from the shaders to mesh loading and rendering, because the Hazel Engine was not initially designed with ray tracing in mind.

Here's a rant for you, one of the challenges I faced during the implementation was the incomplete state of the Vulkan ray tracing validation layers. For instance, I encountered and reported an issue on the ValidationLayers GitHub repository. From my understanding, the cause of this issue was pure undefined behaviour, where the ray tracing stack size was set randomly when it's supposed to be set dynamically but it is not (purely my ignorance). This issue, which was later resolved in SDK 1.3.275, highlighted the need for a more comprehensive validation layer for Vulkan ray tracing. This is far from being the only chanllenge I faced. Using bindless resources was also a challange, simply because of the validation layers. I had many bugs that caused straight up DEVICE_LOST errors that even with NVIDIA's Nsight Aftermath I couldn't track down easily, especially the ones related to acceleration structures. Acceleration structures are not validated well at all. Rant over.

Technical Overview

I had to restructure the acceleration structures multple times during the implementation of my ray tracer, specifically the Bottom Level Acceleration Structures (BLAS). Having each mesh owning its BLAS proved beneficial, at least structurally, and with that idea in place, it was easier to fill the TLAS with BLASes from draw commands originally issued by the scene renderer.

Results and Impact

The primary impact of this implementation is the introduction of global illumination into the Hazel Engine. Without indirect lighting, scenes can appear dull and flat. However, with the addition of global illumination through path tracing, scenes in the Hazel Engine now have a more realistic and visually rich appearance.

Future Work

Looking ahead, I plan to integrate either DLSS or FSR3 to enable the engine to use path tracing for games at high frame rates. Additionally, I plan to incorporate NVIDIA's real-time denoisers to further enhance the performance and quality. The ambitious target is to be able to make games using just the path tracer.

Conclusion

The journey of implementing path tracing in the Hazel Engine has been both challenging and rewarding. It has not only improved the visual quality of the engine but also provided an intuitive way to enhance my mathematical skills. One key takeaway from this experience, as the saying goes, "If it ain't broke, don't fix it" I learned this the hard way, after taking month dealing with undefined behaviour just be cause of a dynamic state in the ray tracing pipeline.

Images

Here are some images that demonstrate the results of my path tracing implementation.
Note that these images are not denoised and they are taken after some amount of pixel accumulation.
Click on an image to view it in bigger size.