I came across some nice articles on this site which have inspired me to try to improve the lighting in my game.
Recently I implemented a “primitive” form of global illumination for the game (I say “primitive”, because while there are surely more advanced techniques out there, it is still far from being simple).
I basically measure reflected sunlight (one bounce) off of objects and add the effects of this to my ambient lighting term. Most of the work happens offline:
- For every point in a world grid, render the scene in all 6 directions with sunlight only, and place these in a cube map
- Integrate the cube map with spherical harmonic basis functions
- Store the spherical harmonic constants in a texture where each texel corresponds to a point in the world grid (currently 1024×1024). I’m assuming a 2.5D world here (so there is just one layer).
For debugging purposes, I can show the generated cube map, the cube map applied to a sphere, and the spherical harmonic constants applied to a sphere (which should approximate the low frequency color variations in the cube map). In the below image, you can see the sphere gets lit by green light reflected off the mossy cliff, and below a bit from the blue water:
At runtime, using the textures that contain the SH constants, I add this term to the ambient light based of the normal of the pixel being rendered. I also scale this by the inverse of the current cloudiness (since it is supposed to represent reflected sunlight).
My plan (not yet fully-implemented), was to render these offline cube maps at 8 different times during the day. Then we would use a blend of the two adjacent (in time of day) snapshots. I got this idea from the GDC 2014 presentation “Assassin’s Creed 4: Black Flag – Road to next-gen graphics“.
Of course, 9 RGB spherical harmonic terms requires 27 bytes to store. If I only use the first 4 SH terms (taking a reduction in quality), then that’s still 12 bytes. At 1024 x 1024, that’s 12MB. Multiplied by 8 times of day, that’s 96MB just for the textures to store the reflected sunlight approximation. As for performance, it adds 6 texture samples to my ambient lighting equation. Offline performance is slow too – I only calculate 20×20 regions on demand right now for testing, but based on the time that takes, it would take about 7 hours to render for the entire map.
I could minimize memory usage by only placing probes in important areas and constructing the textures at runtime based on the nearest probes. But that would be a lot more work.
I suppose this would be worth it if it produced clearly superior results. But the resulting effect is quite subtle:
Overall, the effect is quite subtle for my mostly-outdoor world. It is worth all the effort? It also does nothing to help the case when it’s cloudy and there is no direct sunlight (since this is supposed to represent reflected sunlight). As a result, things are very dull and muddled in that scenario:
Now back to some of the interesting and inspiring articles I found.
The latter article has some really nice images, and describes techniques which are actually similar to stuff I already have (which helps validate my hacks).
Although I don’t currently use the medium frequency occlusion (standard SSAO) because it’s quite expensive, I do have things that are similar to the high and low frequency occlusion.
For many of my objects, I have ambient occlusion terms baked into the geometry (this is the high frequency occlusion). It’s most noticeable in my plants, and improves the look greatly:
You’ll also note the dark area below the plant and near the base of the brick wall. This would be similar to what the article describes as low frequency occlusion. Basically, large objects cause things to be darker below them. Currently, my implementation is specific to terrain – certain objects (tree and walls) add to a ambient occlusion term baked into the terrain. The article describes a more general approach I could use.
So I’ll be looking at some of these techniques to improve my lighting. The first thing I’ll do though is to adjust the albedos of my textures. I think a lot of them are out of whack with each other, making things look not very realistic.
Hopefully I’ll get to a point where I can produce clearly better results, and provide some before-and-after screenshots.