GTAO Sector Integration Formulation in GLSL
Ground Truth Ambient Occlusion (GTAO) improves upon standard screen-space ambient occlusion by formulating visibility as an exact spherical integral over directional slices. The technique decomposes the three-dimensional hemispherical ambient occlusion integral into a series of two-dimensional slice planes aligned with the view vector. Within each slice, ray-marched depth samples determine two maximum horizon angles, allowing the cosine-weighted occlusion integral between those horizons to be evaluated using a closed-form analytical solution. This article outlines the mathematical derivation of this sector integration and provides a direct, production-ready GLSL implementation.
Mathematical Formulation
The standard ambient occlusion equation at a surface point with unit normal \(\mathbf{n}\) evaluates the visibility function \(V(\omega)\) over the upper hemisphere \(\Omega\):
\[A = \frac{1}{\pi} \int_{\Omega} V(\omega) \max(0, \mathbf{n} \cdot \omega) \, d\omega\]
GTAO decomposes this integral into \(M\) rotating 2D planar slices around the view vector \(\mathbf{v} = (0, 0, 1)\) across angular directions \(\phi \in [0, \pi)\). For a given slice at angle \(\phi\), the directions on the slice circle are parameterized by angle \(\theta \in [-\frac{\pi}{2}, \frac{\pi}{2}]\) relative to \(\mathbf{v}\).
The surface normal \(\mathbf{n}\) is projected onto the slice plane:
- Direction vector of the slice in screen space: \(\mathbf{u}_\phi = (\cos\phi, \sin\phi, 0)\)
- Projected components: \(n_t = \mathbf{n} \cdot \mathbf{u}_\phi\) and \(n_v = \mathbf{n} \cdot \mathbf{v}\)
- Projected normal magnitude: \(|\mathbf{n}_p| = \sqrt{n_t^2 + n_v^2}\)
- Angle of the projected normal relative to \(\mathbf{v}\): \(\gamma = \operatorname{atan2}(n_t, n_v)\)
Within the slice, the dot product simplifies via the cosine difference identity:
\[\mathbf{n} \cdot \omega(\theta) = |\mathbf{n}_p| \cos(\theta - \gamma)\]
Because spherical coordinates around the view direction yield the solid angle element \(d\omega = \sin\theta \, d\theta \, d\phi\), the inner angular integral along a single slice between two horizon angles \(h_1\) and \(h_2\) is:
\[I(h_1, h_2) = |\mathbf{n}_p| \int_{h_1}^{h_2} \cos(\theta - \gamma) \sin\theta \, d\theta\]
Using the trigonometric product-to-sum identity \(\cos(\theta - \gamma)\sin\theta = \frac{1}{2}[\sin(2\theta - \gamma) + \sin\gamma]\), the antiderivative \(F(\theta)\) evaluates directly to:
\[F(\theta) = \int \left[ \frac{1}{2} \sin(2\theta - \gamma) + \frac{1}{2} \sin\gamma \right] d\theta = \frac{1}{4} \left( 2\theta \sin\gamma - \cos(2\theta - \gamma) \right)\]
Evaluating across the horizon interval \([h_1, h_2]\) yields the exact closed-form contribution for the slice:
\[I(h_1, h_2) = |\mathbf{n}_p| \cdot \left[ F(h_2) - F(h_1) \right]\]
Horizon Angle Clamping
The searched horizons \(h_1\) (negative direction, \(h_1 \in [-\frac{\pi}{2}, 0]\)) and \(h_2\) (positive direction, \(h_2 \in [0, \frac{\pi}{2}]\)) must be clamped to the tangent plane of the surface to avoid integrating below the surface horizon. The local hemisphere boundary relative to the projected normal angle \(\gamma\) is \([\gamma - \frac{\pi}{2}, \gamma + \frac{\pi}{2}]\).
The clamped horizon angles are:
\[h_1 = \max\left(h_1, \, \gamma - \frac{\pi}{2}\right)\] \[h_2 = \min\left(h_2, \, \gamma + \frac{\pi}{2}\right)\]
GLSL Implementation
The following GLSL functions implement the projected normal calculation, horizon clamping, and closed-form sector integration.
const float PI = 3.14159265359;
const float HALF_PI = 1.57079632679;
// Evaluates the antiderivative F(theta) = 0.25 * (2.0 * theta * sin(gamma) - cos(2.0 * theta - gamma))
float GTAO_Antiderivative(float theta, float gamma) {
return 0.25 * (2.0 * theta * sin(gamma) - cos(2.0 * theta - gamma));
}
// Computes the sector integration for a single slice
// h1: negative horizon angle in radians [-PI/2, 0]
// h2: positive horizon angle in radians [0, PI/2]
// viewSpaceNormal: normalized normal vector in view space (v = vec3(0, 0, 1))
// sliceDir: normalized 2D direction (cos(phi), sin(phi))
float GTAO_IntegrateSlice(float h1, float h2, vec3 viewSpaceNormal, vec2 sliceDir) {
// Project view-space normal onto the 2D slice plane
float n_t = dot(viewSpaceNormal.xy, sliceDir);
float n_v = viewSpaceNormal.z;
float projNormLen = length(vec2(n_t, n_v));
if (projNormLen < 1e-6) {
return 0.0;
}
// Angle gamma between the projected normal and the view axis (0, 0, 1)
float gamma = atan(n_t, n_v);
// Clamp horizons to the surface hemisphere defined by gamma
h1 = max(h1, gamma - HALF_PI);
h2 = min(h2, gamma + HALF_PI);
// Integrate visibility between horizon bounds
float visibility = GTAO_Antiderivative(h2, gamma) - GTAO_Antiderivative(h1, gamma);
// Scale by projected normal magnitude
return projNormLen * max(0.0, visibility);
}To compute the final ambient occlusion coefficient, average
GTAO_IntegrateSlice across all sampled slices \(M\), multiply by \(2\) (accounting for the \(\frac{1}{\pi} \int_0^\pi d\phi\)
normalization factor where the slice spans \([-\pi/2, \pi/2]\)), and subtract the
resulting visibility from \(1.0\) to
produce the final occlusion factor.