How Three.js SSAOPass Calculates Contact Shadows

Screen-space ambient occlusion (SSAO) is a post-processing technique used in 3D computer graphics to calculate real-time contact shadows and soft ambient lighting. In Three.js, the SSAOPass simulates this effect by analyzing the depth and normal buffers of a rendered scene in screen space, rather than performing expensive 3D ray-tracing. This article explains the step-by-step rendering pipeline that SSAOPass uses to determine pixel occlusion and add depth-enhancing shadows where surfaces meet.

1. Generating Depth and Normal Buffers

Before calculating occlusion, SSAOPass requires information about the scene’s geometry from the camera’s perspective. It performs a pre-pass to render the scene’s depth and camera-space normals into separate textures (often referred to as a G-buffer).

These two textures provide the mathematical foundation for calculating spatial relationships in screen space.

2. Creating the Sample Kernel and Noise Texture

To determine if a point on a surface is occluded (blocked by nearby geometry), the shader needs to sample the surrounding area.

3. Projecting and Comparing Samples

For every pixel on the screen, the SSAO shader performs the following calculations:

  1. Reconstruct 3D Position: Using the pixel’s screen coordinates and its corresponding value from the depth buffer, the shader reconstructs the 3D coordinate of the point in view-space.
  2. Orient the Kernel: The shader retrieves the surface normal from the normal texture and uses the noise texture to rotate the sample hemisphere so it aligns with the surface.
  3. Sample the Surrounding Space: The shader loops through the random sample points in the hemisphere. For each sample point, it projects the 3D position back onto the 2D screen coordinates.
  4. Depth Comparison: The shader compares the depth of the sample point with the actual depth stored in the depth buffer at those coordinates:
    • If the sample point is deeper than the geometry in the depth buffer, it is inside an object (occluded).
    • If the sample point is closer to the camera than the geometry, it is in open space (unoccluded).

4. Applying a Range Check

If a sample is occluded by geometry that is extremely far behind or far in front of the target pixel, it could falsely contribute to the occlusion value. To prevent this, SSAOPass implements a range check. If the depth difference between the sample point and the geometry exceeds a set threshold (the occlusion radius), the occlusion contribution is discarded or heavily scaled down.

5. Blur and Composition

The raw occlusion factor is calculated as the ratio of occluded samples to total samples. Because random sampling is used to keep performance high, this raw output looks highly pixelated and noisy.

To resolve this, the SSAOPass applies a bilateral blur filter. Unlike a standard blur, a bilateral blur preserves sharp edges by checking depth and normal differences, ensuring that shadows do not bleed across depth discontinuities (like the edge of a foreground object against a distant background).

Finally, the blurred ambient occlusion map is multiplied by the original scene color, darkening crevices, corners, and contact points to produce realistic contact shadows.