In this previous post, I described how I implemented a primitive form of ambient occlusion for static objects. The gist of it is that I perform an offline rendering step that creates a texture describing the occlusion at each point in the world, at various heights above the terrain.
The thing lacking here is occlusion for moving objects such as the player or other NPCs. Here’s an example under cloudy skies (no directional light):
My offline-generated static occlusion map is (currently) a large 1024 x 1024 texture. To accomplish occlusion for dynamic objects, I need to add their occlusion effect into that texture each frame.
What I actually do is create a much smaller occlusion map (at runtime) that just covers the area seen by the camera. Each frame, we first copy the relevant portion of the static occlusion map into this much smaller runtime occlusion map. Then, we blend in the occlusion effect of dynamic objects. Then we use this texture in our ambient lighting term, instead of the original large static occlusion map. There is obviously an extra performance cost, but it is negligible.
The effect is subtle (as it should be), but now our character appears much more “grounded”:
Currently the dynamic occlusion is just a circular disc with a size and strength, but I could change it to other shapes if I needed. I made an Occluder component which can be added to any entity to cause it to “generate occlusion”.
When I first implemented this, I found that the resolution of my occlusion map was not sufficient for moving objects (the underlying occlusion was jittery as the object moved). Here’s the grid resolution:
So I just doubled the resolution of the runtime occlusion map (but not the original static map). Now the dynamic occlusion disks are drawn at twice the resolution, which is enough to look good.
It also had the nice effect of smoothing out the static part of the occlusion map (since it undergoes some filtering during the upscaling operation):