November 10 2020
By Rubén Torres Bonet
November 10, 2020
Let’s get back to ray tracing with global illumination in Unity. Discover what global illumination is and choose the better fit for your game:
Screen-space Raster GI
Screen-Space Ray-traced GI
Take a second to have a look at the screenshot below.
Can you guess what is this picture missing to be… visually “appealing”?
It’s looking either too bright or too dark. That’s not an easy thing to see in real life.
Because light bounces between surfaces.
These bounces soften the “transitions” between bright and dark areas. And you don’t see that happening in the screenshot above.
Now, let’s see something more realistic.
Have a look below. Use the slider to transition to a more “visually correct” scene:
What’s the difference?
Global illumination (GI). To be more specific, indirect illumination.
Let’s quickly explain how GI works.
Like I said, light rays bounce off surfaces and lose energy in the process (e.g. brightness, a change in color, etc.). Eventually, light will reach your eyes from different surfaces.
In graphics, we distinguish between:
The difference is that GI takes more bounces into account. Not just one.
Direct lighting is easy and cheap to calculate. Indirect lighting, not so much.
So, how do you do indirect lighting* in Unity?
Here are your options.
* I use the words indirect lighting and global illumination interchangeably. In theory, they’re not the same. But in practice, we treat them as if they were.
Let’s build an ultra-simple scene to see and compare the different methods you have in Unity to do global illumination.
Here’s the scene we are building:
Light Bounces Global Illumination Diagram
First, take a deep breath and enjoy my drawing skills.
Now, let’s explain it.
A wall is covering/occluding another wall, which in real-life would prevent light from reaching it.
However, there’s a small gap below.
This bottom gap will allow some sun rays to “escape” the big wall and reach the wall behind after these rays bounce off the floor.
The diagram is clear, right?
Here’s how our baseline (just direct lighting + shadows) looks in Unity.
We won’t use that angle. It’s hard to see the effect of indirect lighting later on.
Here’s a better angle.
The wall behind is quite dark.
No global illumination means light is not bouncing. Our black shadow on that wall remains untouched with no secondary light rays coming in.
Not what you want in your game. Nope.
So let’s improve this scenario and add some GI with our first method: baked global illumination (Unity Lightmapper).
Baking global illumination is the cheapest way to do global illumination.
We do all these expensive calculations offline (in the editor). That process produces textures that we later “paint on top” of our scene.
It takes some effort to get the results you want with this method, as there are many settings to play with. And baking takes time.
But once you get it right, it looks great.
In fact, it’s probably the best-looking method out there to do (static) global illumination.
Well, it only works with static geometry.
I mean, you can use light probes to add some indirect illumination to dynamic (moving) elements. But that also goes with many downsides.
Anyway, let’s see how it looks.
Use the slider to see the evolution from no GI to baked GI.
Just like I promised you, it looks more… correct*.
* You know what I mean. Not the simplified scene per se, but the indirect lighting.
However, did you ever play an interactive game with just static elements?
No, you say?
That’s why we need more options.
One is to use light probes together with baked GI. The issue with light probes is that you need many of them to have smooth indirect lighting transitions. And guess what? That’s going to break batching for good.
Let’s consider instead something else: Screen-space (Raster) Global Illumination.
The idea with SSGI is simple. It’s execution is not.
We analyze the rendered frame data and guesstimate the way light should bounce between surfaces that are within that frame.
What frame data?
Again, we don’t do that for the whole scene. Just for the current rendered image.
Doing this effect in screen-space makes this option affordable in terms of performance.
Use the slider below to find out what SSGI paints on top of our baseline.
A small tint of red.
Not much else.
Here is in detail the indirect lighting that we are adding to our baseline:
That’s a very shy effect. Quite different from baked GI, isn’t it?
That’s one of the limitations of SSGI: accuracy. Two reasons for this:
But hey, it works with dynamic elements. And in real-time 🙂
Can we do better?
What if we used ray-tracing for global illumination?
After all, with ray-tracing we have access to off-screen data. And that removes a limitation of this method.
Let’s check out ray-traced SSGI.
This is getting exciting.
If your project is ray-tracing-ready, you just need to click a checkbox in your screen-space global illumination effect: enable ray-tracing.
Use the slider below to discover it.
Here’s the singled-out effect that ray-tracing GI is adding to our baseline:
Indeed, ray-tracing gets you good-looking indirect lighting. And it works with dynamic elements… in real-time.
The question is, can you really afford ray-traced global illumination in Unity?
Let’s analyze the performance cost of these effects.
Here are some quick tests I ran with:
Here are the average frame times at full-HD resolution:
And here are some of these numbers at 4K resolution:
Why did I take them all with baked GI, you might ask? Well, I removed the baked lightmaps and the entire scene broke for good. Like visually, all was pure white.
Don’t take these times as absolute figures.
After all, it’s a specific scene, a specific software/hardware setup and within the editor.
But you can compare the relative frame times and see how performance scales on this high-fidelity scene.
You get the idea.
Two interesting questions arise (and two subjective answers):
It depends on your game.
But if you put enough optimization effort into it, you can get real-time ray-tracing global illumination in. It’s feasible.
Just run it on performance mode at 1080p and you’ll be fine on most NVIDIA 2000+ cards (and maybe even on the NVIDIA 1000 series).
And the importance of performance brings me to…
How much did you like real-time ray-traced global illumination?
Well, you can get RTGI in your game if you make enough performance room (and all other Unity ray-tracing effects as well).
And nothing better* than my Unity Performance Optimization Checklist for that.
* Unity performance consulting is better (but not many can’t afford it).
Till next time,
~ Ruben (The Gamedev Guru)