1 Comment

Global illumination – improving my ambient light shader

In this post I’ll go over the modifications I’ve made to my ambient light shader to have more realistic “global illumination”.

First let’s look at some of the problems:

 

MissingBounce

 

What’s wrong in this image? The sun is shining on the bright orange cliff on the right, the but the shadowed cliff wall on the left is ignorant of this.

The article here describes a cheap way to mimic this effect: add another light that points in the opposite direction of the sun.

Bounce lighting

So let’s try this out. One place this quickly falls apart is when the sun is shining directly down. The “bounce light” will be coming from below. What happens to our birch trees, which were nicely lit during sunrise? Have a look, the bottom image is with the sun shining down:

 

Overhead

 

The trunks are unrealistically dark because the bounce lighting isn’t really doing anything here: It’s coming from below, and so vertical surfaces are not lit. Granted, the article mentioned above (which is careful to note that its techniques are quick and dirty for demos) says that you should make the bounce light horizontal. When the sun is mostly overhead though, which horizontal direction do you choose?

But ok – let’s see what it looks like when the sun is at a lower angle (where a horizontal bounce direction makes more sense). Here’s a directional (sun) light only, near sunrise:

 

DOnly

 

 

Now let’s put another directional light from the opposite direction with (Suncolor * SceneAlbedo). This is our “bounce light”:

 

BounceOnly

 

And now combined:

 

BothDAndBounce

 

Well that’s nice, except we see there is a dark area on the tree trunk where it is affected by neither light. That might be accurate if you were holding a mirror up to the sun, but not when the sun reflected off the whole scene. One option is to have the bounce light “wrap around” past 90 degrees a bit. That does do a decent job of fixing the above two issues.

But let’s take a closer look at what is actually happening: Sun is shining down, and lights up the scene. Let’s make a simple assumption that the scene is flat ground:

 

SunShineBounceDiagram

 

Assuming the sun is lighting up an “infinite” area around the tree, the bounce light affecting the tree will basically be a hemispheric light. It’s like the tree is lit by a cubemap, the bottom half of which is (SunColor * SceneAlbedo) modulated by the angle at which the sun is hitting the ground, and the top half of which is black (when considering only the bounce lighting, not the original sunlight).

This provides us with a little bit more real physical basis for the “wrap around” I mentioned for the bounce light:

 

float3 bounceLightColor = sunlightColor * sceneAlbedo * saturate(dot(sunDirection, groundUpVector));
float3 bounceLight = bounceLightColor * dot(surfaceNormal, groundUpVector) * 0.5 + 0.5;

 

Now the trunks are lit up by sunlight bouncing off the ground:

 

HemisphereBounceLight

 

Now back to the canyon image at the beginning of this article. We wanted the light reflected off the opposite canyon wall to light up the shadowed wall a bit. Well, this hemispheric light isn’t going to do much for that – our light is coming from the ground hemisphere, and we’re essentially ignoring the sun direction (or rather, it’s only being used to determine the strength of the sunlight reflecting off the ground).

To get around this problem, we can use a modified ground plane. We’ll tilt it slightly towards the sun. This arguably has a physical basis – the sun isn’t just reflected off flat ground, it’s reflected off of “above ground” objects like other trees and rocks. Yes, there are objects on both sides of the point we’re lighting, but only the ones on the opposite side of the sun really contribute to bounce lighting.

 

TiltedGroundNormal

Tilted ground surface for bounce lighting.

 

The tilted ground normal is calculated like so:

Vector3 tiltedGroundNormal = Vector3.Normalize(actualGroundNormal * C_VALUE + directionToSun);

 

I use a value of 0.75 for C_VALUE currently.

 

So here’s one of those cliffs again, showing the directional light and with and without the bounce light (I’ve also turned off the overhead hemispheric ambient light, so that’s why the top image is completely dark in the shadowed areas):

 

WithAndWithoutBounce

 

 Scene albedo

I’ve used the term scene albedo a number of times so far. What is it? Well, I started off using a brownish green color with an albedo of about 0.2. 0.2 is a typical value for the grass, dirt, cliffs, etc.

I knew using a fixed scene albedo would be wrong, but the bounced light is a fairly minor component compared to the sun itself, so it’s not terribly noticeable. Except when the actual ground has a very different  albedo – such as snow.

 

FixedSceneAlbedoSnow

On the left, the birch trunks are nicely lit by the rising sun. At noon, on the right, the trunks are way too dark, even though they should be lit by our bounce hemispheric light.

 

So I decided it was time to make a world albedo map. This is done by rendering the world from above, chunk by chunk, stitching it all together, and blurring it significantly. Snowfall is dynamic in my world though (what a problem that is!), so I also need to make a rough estimation of how much a particular piece of ground is affected by snow (e.g. water and cliffs aren’t), and then combine that with the current snowpack in that area. The result of doing this is that the trees are much better lit in bright snow:

 

VariableSceneAlbedoSnow

 

Just for kicks, let’s compare some test objects: one out in the water vs one in the snow:

 

TwoCylinders

 

 

 

These are backlit, so most of the light we’re seeing on them comes from our ambient term (which includes scene albedo). If we compare them side by side, we can see the significant different in brightness:

 

TwoCylindersCompare

 

So this is a very very rough approximation of objects inheriting some color from their surroundings. If I paint the ground bright colors, it’s more noticeable:

 

InheritColor

 

It’s important to note that this is not directional though. The cylinder in the middle is a mix of green and magenta even though from this angle it would be greenish in the real world. For a directional solution we could use spherical harmonics. I describe my experience with that here.

The other ambient term

So far we’ve been talking about the sunlight bounce term. There’s also the ambient term that describes how the object is lit by the sky (as opposed to the sun/moon). With everything we’ve talked about so far, this one becomes pretty obvious. For this I use a hemispheric light that goes from the SkyColor on top to SkyColor * SceneAlbedo on the bottom. This is basically what I did before, except now we have the benefit that it gets some color from its surroundings.

Let’s finish with a foggy nighttime scene with the dark sides of the conifers lit by bounced moonlight:

 

Night

 

 

Oh wait, one more. The following image shows our three main lighting terms, and the final image with them composed together:

 

Demo

 

Advertisements

One comment on “Global illumination – improving my ambient light shader

  1. […] the fixes to my HDR pipeline and albedo, the ambient lighting/global illumination, ambient occlusion map, and various other tweaks, I thought I’d show some […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Space Quest Historian

Let's Play's, Podcasts, and General Adventure Game Goodness

Harebrained Schemes

Developer's blog for IceFall Games

kosmonaut games

Development blog of "Bounty Road"

Halogenica

Turn up the rez!

bitsquid: development blog

Developer's blog for IceFall Games

Game Development by Sean

Developer's blog for IceFall Games

Lost Garden

Developer's blog for IceFall Games

Memories

Developer's blog for IceFall Games

Casey's Blog

Developer's blog for IceFall Games

Blog

Developer's blog for IceFall Games

Rendering Evolution

Developer's blog for IceFall Games

Simon schreibt.

Developer's blog for IceFall Games

Dev & Techno-phage

Do Computers Dream of Electric Developper?

- Woolfe -

Developer's blog for IceFall Games

Ferrara Fabio

Game & Application Developer, 3D Animator, Composer.

Clone of Duty: Stonehenge

First Person Shooter coming soon to the XBOX 360

Low Tide Productions

Games and other artsy stuff...

BadCorporateLogo

Just another WordPress.com site

Sipty's Writing

Take a look inside the mind of a game developer.

Jonas Kyratzes

Writer, game designer, filmmaker.

%d bloggers like this: