Simple Game Engine focusing on NVIDIA Vulkan Ray Tracing
With the introduction of Nvidia RTX hardware, ray tracing is now viable as a general real time rendering technique for complex 3D scenes. Leveraging this new technology, we present Raygun, an open source rendering, simulation, and game engine focusing on simplicity, expandability, and the topic of ray tracing realized through Nvidia's Vulkan ray tracing extension.READ FULL TEXT VIEW PDF
Ray tracing is a technique for generating an image by tracing the path o...
Ray tracing on GPUs is becoming quite common these days. There are many
This user guide outlines the use of TIM, an interactive ray-tracing prog...
NVidia RTX platform has been changing and extending the possibilities fo...
We introduce Proceduray, an engine for real-time ray tracing of procedur...
We present a simple, novel method of efficiently rendering ray cast soft...
While ray tracing has become increasingly common and path tracing is wel...
Simple Game Engine focusing on NVIDIA Vulkan Ray Tracing
In the beginning, Nvidia RTX  could only be used via Nvidia OptiX , their dedicated ray tracing application framework, or via Microsoft DXR . As many developers were hoping for a more open approach to this technology, Nvidia released an (experimental) extension for Vulkan  with their 411.63 graphics driver . This advancement in technology is what makes Raygun possible.
At the time consumers got their hands on Nvidia RTX capable graphics cards, none of the modern, publicly accessible video game engines allowed using Nvidia RTX for the whole rendering process. Initially we looked at Unity222https://unity.com, Unreal333https://www.unrealengine.com, and Godot444https://godotengine.org just to find out that RTX is not yet available in these engines. Unity’s engine is closed source, Unreal is far too large for us to add RTX support in a tangible amount of time, and Godot has not made the transition (from OpenGL) to Vulkan yet. This spawned the incipient idea of Raygun. While Quake II RTX , an implementation of RTX ray tracing using the Quake II engine, already existed at this time, we decided to create our own engine using C++ and modern libraries, focusing on a slim core with expandability in mind.
Nvidia RTX ray tracing works in two steps. First, a two-layered data structure is instantiated with the vertex information (and transformation matrices) of a scene’s 3D models. Next, a dispatch call initiates the ray tracing process causing the execution of a ray generation shader. This shader controls the initial casting of rays. Different shaders are invoked depending on whether a ray intersects with any triangle (closest hit shader and any hit shader) or not (miss shader). This is done for each ray. The invoked shader has the option to spawn additional rays, continuing the ray tracing process555The number of these recursive steps is limited by hardware parameters and ray payload size.. Each ray can carry a payload (limited, user-defined data) between shaders.
Raygun’s main goal is to combine a Vulkan-based ray tracer with other systems typically found in video game engines (e.g. scene graph, physics engine, audio system). It is designed to be used as malleable framework for creating simple, ray traced 3D video games. The division between game code and engine code can be maintained, as suggested by the file structure666See subfolders raygun vs. example..
Abstractions over systems are kept to a minimum so that access to a system’s more exotic features is retained. For instance, we expose the underlying objects of the physics engine so that the physics engine’s full potential can be leveraged without adjusting multiple layers of abstractions. This benefits not only rapid prototyping, but also enables one to swap out systems with little effort at the start of a new project (e.g. replacing Nvidia PhysX777https://developer.nvidia.com/physx-sdk with Bullet888https://github.com/bulletphysics/bullet3).
As Vulkan is cross-platform, Raygun targets Windows as well as Linux. Third-party libraries have been selected accordingly. CMake999https://cmake.org is used as build system.
In addition to the commonly used C API, the Khronos Group also provides a C++ API . Although using the C++ API can introduce breaking changes across Vulkan SDK versions, it is still preferred to the C API due to Vulkan being a low-level API. Rendering a simple cube in Vulkan requires a moderate amount of code (compared to high-level APIs like DirectX or OpenGL) . Code becomes more manageable with the C++ API as language features like RAII101010https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization, generic programming, and exceptions are utilised. While implementing the render system, we introduced thin wrappers around commonly used Vulkan objects (e.g. buffers, descriptor sets) to improve the developer experience even more.
The ray tracer is based on the official Nvidia Vulkan ray tracing tutorial . The code has been rewritten using Vulkan’s C++ API and now supports rendering multiple objects. To highlight the benefits of ray tracing over rasterization we extended the shader code to simulate reflection and refraction in addition to shadow casting.
But Vulkan is not just a 3D graphics API, it also provides support for compute workloads. Considering this, a compute system has been integrated into the engine alongside the render system. Putting this compute system to use, we also added screen-space roughness approximation and fast approximate anti-aliasing (FXAA).
Shaders are implemented using OpenGL shading language (GLSL) and compiled to SPIR-V during built-time. Additionally, shaders can be tweaked, recompiled, and reloaded during runtime.
A profiler has been fitted into the engine, using Dear ImGui111111https://github.com/ocornut/imgui for visualisation. Material properties, used by shaders, can be manipulated in real time via an on-screen material editor.
Due to the complexity of Vulkan’s API, introducing bugs is quite likely. To contain this trouble, the Vulkan SDK features a validation layer that intercepts and checks Vulkan API calls at runtime. This mechanism is enabled in Raygun debug builds. Note that, Vulkan also allows one to tag Vulkan objects with a custom string so they can be identified with ease in profilers and logging output.
Nvidia PhysX is used as physics engine because of its industry roots, license (BSD 3), and visual debugger support. We added a simple mechanism to register trigger and collision call-backs for physics actors and also forward log messages to the Raygun-wide logging infrastructure121212Using spdlog, see https://github.com/gabime/spdlog.
An OpenAL131313https://www.openal.org based audio system allows playing of positional sound effects as well as background music. Audio sources are manipulated through a thin wrapper class as OpenAL uses a very primitive C API. Sound files are Opus141414https://opus-codec.org encoded and stored using the Ogg151515https://www.xiph.org/ogg container format.
The aforementioned systems are connected via a basic scene graph. This scene graph is to be used as tree of entities (i.e. game objects) where each entity holds instance specific data for all systems (e.g. model and materials used for rendering). Strictly speaking, Raygun does not use an entity component system (ECS)161616https://en.wikipedia.org/wiki/Entity_component_system architecture, we just adopted the terms entity and system.
For in-game user interfaces we came up with a UI generator that loosely follows ImGui’s API. Predefined widgets (e.g. labels, buttons, sliders) are programmatically composed and automatically arranged. All UI widgets are ray traced 3D objects — no textures needed.
Window management and input processing is handled through GLFW171717https://www.glfw.org. Mouse and gamepads are supported, despite using only keyboard input in the provided example application.
3D models are loaded from COLLADA181818https://en.wikipedia.org/wiki/COLLADA files using custom JSON documents for material properties. To minimize material data duplication, materials can inherit properties from other materials.
A dedicated resource manager allows caching of loaded resources.
Raygun is considered feature complete and enables one to experience ray tracing in a developer friendly environment. Some parts of the code-base may receive additional clean-up and code-quality improvements in the near future. We intend to keep the engine up-to-date with respect to the Vulkan SDK, leveraging new features given their applicability.
The Khronos Group is working on a vendor independent ray tracing extension for Vulkan.  As this addition will be based on the currently used, experimental Nvidia extension, migration is expected to be straightforward. Using a vendor independent extension empowers Raygun to also run on non-Nvidia platforms.
C++20 will bring lots of interesting features to the language, giving us additional opportunities to further improve code-quality. Especially modules, designated initializers, and ranges promise to greatly enhance code composition. Coroutines, although stack-less, could facilitate the creation of a scripting system using C++ as embedded domain-specific language (eDSL).