Still working on global illumination. Ported most of the DDGI implementation over from JS side to GPU. Still struggling with various bugs. Here are a few screenshots of current progress:
Here are some of the artifacts:
You can clearly see the probe boundaries and there are various leaks.
Oh yeah, I’ve spent a bit more time on the sky. Realized that it’s pretty important for lighting, especially for outdoor scenes. It’s a fascinating topic, how the sky color is all about absorption and scattering due to interplay of molecules and photons. I’ve incorporated sky into both the direct lighting ad well as path tracer that powers the GI. Here are a few pretty renders from the path tracer with the sky enabled.
Path Tracer
![Screenshot 2024-09-04 054511|500x500]
(upload://3pZDBmN1hn0WNKBr2cj7YyMIYvW.jpeg)
for those who are interested all renders are 32 samples only (hence the noise).
Global Illumination is Hard
The difficulty with GI is that it’s a multi-variate optimization problem. That is - there are multiple things you want to maximize, like realism, shadows, highlights. In reverse sense, things you want to minimize like noise and leaks (shadow/light where they aren’t supposed to be).
One one end of the spectrum you have a full path tracer, which is slow as sin and can’t really be made “fast”. There are tricks that use various functions to guide rays, and caching strategies, but inherently it’s a statistical simulation and those are notoriously non-linear in excution time. For things like solving a differential equation with a couple of variables using gradient descent - we know we can get a good result in just a few steps, but for path tracing the “number of variables” or the conditionality of the problem is huge, really really huge. So path tracing will never be the go-to technique for real-time rendering where performance is concerned. There, I said it.
My path tracer can trace ~9,000,000 (9 mil) random paths per second, with up to 3 bounces. That’s on RTX 4090. It’s slow, too slow for real-time. 1080p resolution is typically 1920*1080, or 2,073,600 (2mil) pixels. I can trace ~4 frames per second, in reality it’s quite a bit better as paths are not random if we trace from the screen, most of the rays will have excellent alignment so GPU cache usage will be good.
Say we can trace 20 frames, say you’re running a proper RTX api and not a custom software implementation as I do, say you get 100 frames, or even 200. That sounds good, except the actual image quality will be awful. For a relatively clear image in good lighting conditions you need ~500 paths (samples) per pixel, so that 200 budget brings us squarely back to 0.4 FPS.
A good denoiser with temporal component can make do with ~16 samples per pixel. We’re back in real-time range at 12.5 FPS. But this will only hold true as long as we’re in good lighting conditions. As soon as lights get smaller and further away - required number of samples grows exponentially.
I believe a path tracer with tricks can be used in specific lighting conditions, eventually. But that’s probably a couple of generations off. But even then, we’ll be relying on tricks.
Famously unreal’s lumen is ray tracing right? Wrong, it’s a clever mix of SDF ray marching and surface caches. Even then, it’s pretty demanding on hardware and is unstable in low-light conditions and in cases of small light sources.
On the other end of the spectrum, we have irradiance fields, such as what I’m working on. The idea is to capture lighting in 3d space using potentially expensive techniques like path tracing and sample that “field” during shading. No path tracing necessary once the field is captured.
Irradiance Field
Neat, except that field will be of low resolution, it kind of has to be, otherwise we’re back to path tracing the image, the thing we wanted to avoid. So, that low resolution creates problems for us, just like if you have a very low resolution texture and try to represent accurate details with it - you’ll see more and more problems as resolution goes down, same with the irradiance field.
Here’s a very interesting set of screenshots from the original DDGI paper:
It’s very clever and subtle, so subtle you might not catch it. DDGI paper promises a robust solution to global illumination, no tweaks, no fiddling. But let’s have a look here:
classic probes, we see the light leakage all over - bad, yeah.
biasing via normal makes things smoother, kind of, but there are more leaks now. The smoothing comes partially from oversampling probes that have nothing to do with us. Okay, let’s see where this goes.
Using a low-resolution depth map we can figure out which probes are behind the surface we’re trying to shade, it’s not perfect, you can see the spikes indicating so-called self-shadowing, similar to errors in standard shadow maps. I mean, it’s better now in the sense that we have less light leackage that we started with, but I think every sane person will agree that this looks ugly and is way more distracting than the “classic”. Here it again for comparisson:
So how do we solve it? Ah, well, with a fudge factor of course!
el voilá
Here’s the relevant bit from the paper
Let’s take a look at the screenshots from the paper itself:
looks good, I can see some errors, but nothing major.
Looks fine as a screenshot, but if you look closer there are leaks, and quite major ones:
here’s the image without DDGI for comparisson
Here’s a more interesting one:
lets enhance first row
If you look closely you’ll notice that none of these are the same, there are artifacts in every one of them and different ones at that.
And if you think I’m cherry picking, here is the second row:
in case it doesn’t pop out to you:
This doesn’t mean that probes have no chance, they still have excellent performance characteristics. This does mean that probe placements are quite important and that pesky self-shadowing bias needs to be addressed.
DDGI in Shade
Back to shade. To establish a baseline here’s what render looks like without GI:
And for comparrison, here is the path traced version with 512 samples/pixel, we’ll assume that to be our ground truth.
Let’s try different grid resolution for probe placement:
Pica Pica
2x
4x
x8
x16
x32
Kitchen
off
ground truth
x2
x4
x8
x16