
Motivation
Ambient occlusion is a
lighting technique used to give models a global illuminationlike
effect, as if it were lit from the entire hemisphere (rather than a
point light). A set of visibility samples are collected from the
hemisphere above a point, and a scalar "ambient occlusion" value is
computed based on the percentage of unoccluded samples. For a nice
overview of ambient occlusion, check out Steve Hill's article
in Gamastura. Some other very good ambient occlusion tutorials
have been written by Zhang Jian, Bob Moyer,
and Andrew
Whitehurst.
Approaches
There are two basic approaches for computing ambient occlusion. The
first is ray tracing; simply shoot out rays in uniform pattern across
the hemisphere. The percentage of rays that hit geometry is divided by
the total number of rays to get a scalar value. This could also be done
with hardware rendering by placing a camera at every triangle and counting the
percentage of the hemisphere that is visible. Since this requires computing
occlusion at every triangle, this approach is in general O(N^2), although with
efficiency structures (such as bounding volume hierarchies) it can be reduced to O(NlogN).
The second approach is an "outsidelookingin" approach. The model is
rendered from a set of M random points on the sphere surrounding the
model. The occlusion information is stored for each viewpoint, and
averaged. The complexity of this approach is O(N*M), or O(MlogN) with efficiency structures. Since M is
generally much smaller than N, this makes it interesting for realtime work. Since this
is the approach I am interested in, let me explain the algorithm in more detail.
OutsideLookingIn Algorithm
for( some number K camera samples ) place a camera at a random point on the sphere surrounding the object // First pass  fill the depth buffer glClear(); glEnable( GL_depth_compare ); for( each triangle i in model ) render( tri[i] ); end
// Second pass  render into the depth buffer w/occlusion queries. // This will give the number of unoccluded fragments for( each triangle i in model ) glBeginOcclusionQuery( i ); render( tri[i] ); glEndOcclusionQuery(); end // Get occlusion queries with depth testing
for( each triangle i in model )
triangle[i]>TotalFragments = glGetOcclusionQuery(i);
end
// Third pass  render without depth testing. // This gives the total number of fragments (unoccluded + occluded) glDisable( GL_depth_compare ); for( each triangle i in model ) glBeginOcclusionQuery( i ); render( tri[i] ); glEndOcclusionQuery(); end // Get occlusion queries without depth testing
for( each triangle i in model )
tri[i]>VisibleFragments = glGetOcclusionQuery(i);
tri[i]>AmbientOcclusion += tri[i]>VisibleFragments / (tri[i]>TotalFragments * K);
end
end
Using a threepass algorithm like this means that you don't have to
store
off depth maps and use depth comparison. As a tradeoff, however, you
have to deal with large amounts of occlusion queries. In fact, you
don't want to code the algorithm as I did above; the hardware will
stall while waiting for the each of the occlusion queries to finish. A
better way to implement this is use two (or more) batches of occlusion
queries, and pingpong between them.
More importantly, there is a neat advantage that you get from using
occlusion queries in this manner. Notice that above I divide the
VisibleFragments by the TotalFragments after each pass. However, there
is an alternate method; maintain a running total of the fragments
across all viewpoints, and only divide at the end. Interestingly
enough, this gives you a cosineweighted ambient occlusion (because
nTotalFragments is larger when you face the polygon straighton than at
an oblique angle).
Bent Normal
Bent normals are an extension to ambient occlusion to allow more
intesting lighting. The idea is to average the unoccluded
vectors and use this vector (which is "bent" in the direction of
unoccluded hemisphere) to look up in an environment map.
Results
These results aren't very impressive. I'll try to post more results as
I get them.
Ball & Ground Plane 

Head Model 
CosineWeighted Head Model 


email at coombe@cs.unc.edu.

