Ambient Occlusion

Remember how when you shade a fragment you consider only the fragment and the light source? You don't consider any other surfaces. That means some features are missing from your scenes:

There are workarounds to these limitations. You've seen some already. You added an ambient term to give the feeling that light is bouncing around into areas that the light doesn't reach. You used environment mapping to make an object reflect the skybox.

If you want to add subtle shadows to your scene, you may use a technique called ambient occlusion. You compute ambient occlusion at a location on a 3D model by placing a hemisphere along the normal at the location. Then you cast out many rays in all directions within the hemisphere. If a ray hits another part of the model, then you have found an occluding surface that blocks some light from reaching the location. If all rays hit occluders, then the location's ambient occlusion is 0. If no rays hit any occluders, then the ambient occlusion is 1.

Ambient occlusion is expensive to compute. It is therefore often computed in the 3D modeling tool, long before it reaches your renderer. The model is unwrapped onto a texture that is initially blank. Then each texel is visited. A hemisphere is placed at the 3D position that corresponds to the texel, and the rays are cast. The proportion of rays that do not hit occluders is stored in the texture.

Here's the ambient occlusion map for a house model:

Ambient Occlusion Map

The roof on the top left has hardly any occlusion, while the eaves are deeply in shadow.

During rendering, the fragment shader looks up the ambient occlusion from the ambient occlusion map and multiplies the lighting terms by it. The depth cues from ambient occlusion are surprisingly strong, even with no shading applied:

Rotate the house and zoom in and out. Use the controls to contrast the ambient occlusion map to diffuse shading.

Like the other workarounds, ambient occlusion is not grounded in reality. The computed occlusion terms are really just probabilistic measures of whether or not light reaches a fragment. No light sources are even considered when the texture is populated in the 3D modeling tool.