CS779 Final Project: Real-Time Ambient Occlusion

Ian Purvis and Luke Tokheim


Project Description:

Our project goal was to create a real-time environment renderer using precomputed ambient occlusion values. The diffuse lighting for the scene is defined by an environment map to model realistic outdoor or ambient light. We precompute a per-vertex occlusion value and bent normal. The occlusion value attenuates the vertex color, creating a Gouraud shaded shadowing effect. The bent normal is used to do lighting look ups from the environment maps. It is a more realistic interpretation of the diffuse light path. We use methods similar to Radiosity Form-Factor evaluations to compute the occlusion values and bent-normals.


Hemisphere sampling:

For each vertex, we shoot rays in the hemisphere about the normal. Each ray that does not intersect the object, contributes to the amount of ambient light that vertex receives. The occulsion value for a vertex is the normalized sum of all rays that make it out to the environment. We also store the the median ray that did not intersect the object as the bent normal. Intuitively, this ensures that the diffuse light looked up in the environment map comes from the direction of highest exporsure to ambient light.

To sample in the hemisphere we use a uniform sampling on the unit disc about the origin. Then we project each sample on the disc up to the hemisphere, which gives us a cos(θ) weighed sampling on the hemisphere, where θ is the angle between the normal and the sample ray.

Hemisphere about the vertex normal Hemisphere sampled with Bent Normal
Figure 1: Hemisphere sampling about a vertex normal. The occlusion value for this vertex would be about 2/3. Figure 2: The bent normal is the median ray that did not intersect the mesh.


Hemicube sampling:

Another approach to sampling the vertex occlusion value and bent normal is to use the hemicube. The advantage of this is that each face of the hemicube can be rendered in OpenGL, instead of raytracing in the hemisphere. The pixel contribution has to be weighted by the amount of project area on the hemisphere associated with that pixel. We used a weighting function defined as: w( x, y, z ) = y / PI * (x^2 + y^2 + z^2)^2 where y is the up vector.



Cutbox, textured Cutbox, textured and environment mapped
Cutbox, textured, environment mapped, per-vertex occlusion values Cutbox, textured, environment mapped using bent normal, per-vertex occlusion values
Figure 3: From left to right, from top to bottom, an object rendered with a) texture only, b) texture and environment mapping, c) texture, environment map, and per-vertex occlusion, d) texture, environment mapped using bent normals, and per-vertex occlusion. Please note that the environment map has a slit of light across the top, and is darker on the sides, which explains the darkness on the sides of the box.


Aerodyne, environment mapped
Aerodyne, environment mapped using bent normal, per-vertex occlusion values
Figure 4: From top to bottom, a complex object rendered with a) environment mapping, b) environment mapping using bent normals, and per-vertex occlusion. Note the shadowing in the wheel wells.


Cola, textured, environment mapped Cola, textured, environment mapped using bent normal, per-vertex occlusion values
Figure 5: From left to right, an object rendered with a) texture and environment mapped, b) texture and environment mapping. An artifact of using a per-vertex method is shown in the shadow bleeding from underneath the pop tab.



Download the source: ambenv.tar.gz

Download a Win32 binary: ambenv-win32.zip

This project depends on: Simple DirectMedia Layer, TIFF Library, 3D Studio File Format Library.

We have built it in Windows (MSVC and MinGW), and Linux/MacOSX with gcc.



1. Landis, H. Production-Ready Global Illumination. Siggraph 2002 Course Notes.

2. Larsen, B. Radiosity with Hemicube code example. http://www.imm.dtu.dk/~bdl/02561/radiosity/