Running on Raygun

01/27/2020 ∙ by Alexander Hirsch, et al. ∙ 0

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.



There are no comments yet.


page 1

Code Repositories


Simple Game Engine focusing on NVIDIA Vulkan Ray Tracing

view repo
This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

1 Introduction

In the beginning, Nvidia RTX [7] could only be used via Nvidia OptiX [6], their dedicated ray tracing application framework, or via Microsoft DXR [5]. As many developers were hoping for a more open approach to this technology, Nvidia released an (experimental) extension for Vulkan [2] with their 411.63 graphics driver [9]. 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 Unity222, Unreal333, and Godot444 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 [1], 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.

2 Architecture

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 PhysX777 with Bullet888

As Vulkan is cross-platform, Raygun targets Windows as well as Linux. Third-party libraries have been selected accordingly. CMake999 is used as build system.

2.1 Render System

In addition to the commonly used C API, the Khronos Group also provides a C++ API [3]. 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) [8]. Code becomes more manageable with the C++ API as language features like RAII101010, 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 [4]. 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 ImGui111111 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.

2.2 Other Systems

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

An OpenAL131313 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 Opus141414 encoded and stored using the Ogg151515 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)161616 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 GLFW171717 Mouse and gamepads are supported, despite using only keyboard input in the provided example application.

3D models are loaded from COLLADA181818 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.

3 Future Work

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. [10] 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).