Global Illumination & The Switch To DirectX 11
A progress update.
The process of implementing GI into the engine proved to be non-trivial, but I’m very happy with the results so far. Check out these two videos where I show comparisons with and without GI and a animated compilation of the baking process in the test levels.
The implementation is very inspired by the DDGI method, with the main differences being that the probe lighting is baked and that I’m using the engine’s built-in rasterizer instead of writing a raytracer implementation for the light probes. The scene is rasterized into a cubemap which is then packed into an atlas using octahedral mapping, which I use both for the irradiance, and the visibility test which prevents light from leaking.
The results look promising. There are some artifacts with the probe visibility map, but I’m already very happy with the results so far and wanted to share them. The next step for the lighting would be to improve the probe placement situation. As you see in the videos, most probes are in the sky, far from any geometry, and end up not being useful at all, and some probes can end up inside geometry which can cause sadness.
Another area requiring improvement is the probe visibility test. The probe resolution for both the irradiance map and the visibility map currently is 32 x 32 pixels, which is more than sufficient for the irradiance part, but I suspect will not be enough for the visibility part.
I want to thank my friends Daniel and Moritz for their help. Daniel pointed me towards the DDGI method and helped me understand the idea, and Moritz was always there when I had questions. Without them, it would’ve taken me a much longer time to reach the same place. Now that I have the experience of implementing it and dealing with the problems along the way, I find myself confident in my understanding of it and how to implement it.
Spending so much time deep in the implementation of these graphical effects (and also the physics engine) affects how I see the world, in the sense that I start noticing details everywhere. The way light bounces off surfaces, how shadows fall in corners. Suddenly, staring at a wall in an alley becomes fascinating. This quote1 from Masters Of Doom puts it in a very nice way:
...after so many years immersed in the science of graphics, he [John Carmack] had achieved an almost Zen-like understanding of his craft. In the shower, he would see a few bars of light on the wall and think, Hey, that's a diffuse specular reflection from the overhead lights reflected off the faucet. Rather than detaching him from the natural world, this viewpoint only made him appreciate it more deeply. 'These are things I find enchanting and miraculous,' he said, 'I don’t have to be at the Grand Canyon to appreciate the way the world works. I can see that in reflections of light in my bathroom.'
The Switch To DirectX 11
Up until now, everything was done in OpenGL, and the plan was to stick with it, and ship the game with it as the rendering backend on PC. However, after a very frustrating incident, the camel’s back broke and I decided to port everything to DirectX 11 and abandon OpenGL.
The two APIs are very comparable in many ways, well to clarify I’m talking about OpenGL 4.0 and upwards. But they represent different design ideologies, and it’s been very interesting working with both and observing the different design decisions. The most apparent design ideology being that OpenGL prioritizes backwards compatibly (You still can run ancient OpenGL code), while DirectX introduce breaking API changes.
I found the experience of using the DX11 API to be very positive, and without diving into the API design specifics, by and large, I find the API design decisions to be high quality and better than OpenGL. However, I didn’t make the switch just because DX11 has a marginally better API.
It’s because of the support ecosystem.
Let’s begin with the fact that there is no proper validation layer for OpenGL. DirectX 11 has one built-in to the OS, which is easily the best part of the API. With very descriptive warning/error messages, and suggestions. To add to that, graphics debugging utilities like RenderDoc, Nsight, and PIX offer a much deeper support for the DX11 pipeline, with features like additional validations, GPU timings, and shader debugging.2
The support situation also extends to GPU vendors. Intel has been notorious for having terrible OpenGL drivers. I happen to have 3 development machines, with my primary machine having an Nvidia GPU, and the two others having Intel and AMD GPUs. I found it a regular occurrence where the game does not run and look the same on the 3 machines, despite the code being identical. I have a comical list of these differences but the funniest one in my opinion is the following case:
#version 400 core
...
void main () {
vec3 input = texture(inputTexture, uv).rgb;
result.rgb = coolTonemapping(input);
...
}This GLSL shader compiles and runs just fine on Nvidia drivers. But it doesn’t even compile on Intel/AMD. Because input is a “reserved” keyword.
Tangent on the subject of OS support:
On Windows, the lack of support when using OpenGL becomes apparent from the very first second you want to use the API. Since you have to do the wiggle dance™! For those of you who are not familiar with the wiggle dance™3, it’s the process of loading OpenGL driver/functions into your application. Where you have to create a fake/dummy window, just to setup an OpenGL context and load a few function pointers, which then you have to actually load the OpenGL function pointers that you need, individually, one by one. That’s why most people use libraries like GLAD to automatically setup OpenGL.
In contrast, for DX11, all you have to do is to include the headers, and link with the DX11 libraries, and you’re good to go! And setting it up is only a few function calls. In my case, since the renderer lives in a hot-reloadable DLL module, DX11 makes it very nice and easy to initialize the renderer in the DLL module and keep all the code in one place, whereas with OpenGL, the launcher has to setup OpenGL because of some OpenGL invisible global state magic, that goes awry when the DLL is unloaded.
If OpenGL had better support, I would’ve likely not migrated to DX11. At the end of the day, the APIs share many similarities. Also it’s important to consider that OpenGL can actually do more than DX11 through their extension system, and you’d have the ability to do things like bindless textures, mesh shaders, and more. Finally, using OpenGL does not automatically mean slow and buggy. There are modern games running on OpenGL showing it works just fine and is performant.4
Despite all of its downsides, I still really like OpenGL5 and it holds a special place in my heart. I think it deserves its place among the APIs and is worth keeping alive.
Quote taken from: https://theorangeduck.com/page/real-time-graphics-virtual-reality I also recommend this post :)
According to the RenderDoc changelog, version 1.42 which was released a few days before this post was written adds limited support for shader debugging on OpenGL.
Doom 2016, and Teardown ship with a very good OpenGL backend. Also check out this presentation from Nvidia: How Modern OpenGL Can Radically Reduce Driver Overhead




