The G term is the unglamorous half of the BRDF. It's not where the interesting visual work happens, which is probably why it gets the least attention.
What It Solves
At grazing angles, surfaces have a tendency to blow out. Without G, the BRDF's denominator drives energy up faster than the specular lobe can compensate, and surfaces start emitting light they shouldn't. You've probably seen it: a low-roughness material at a shallow viewing angle that flares unnaturally bright along its silhouette. That's what happens when G is wrong or missing.
The cause is physical. At low angles, microfacets start blocking each other. A facet that would otherwise contribute to the reflection might be hidden from the viewer (masking) or blocked from the light source (shadowing). G accounts for both.
Smith (1967) showed that masking and shadowing can be treated independently, splitting G into two identical single-direction functions -- one for the view direction, one for the light:
What Ships in Real-Time
The exact Smith-GGX formulation involves a square root per term. Epic's UE4 approximation avoids it by remapping roughness to a k value:
The two k values are not interchangeable -- IBL and direct lighting need different remappings. In GLSL:
float G1_SchlickGGX(float NdotV, float k) {
return NdotV / (NdotV * (1.0 - k) + k);
}
float G_Smith(float NdotV, float NdotL, float roughness) {
float r = roughness + 1.0;
float k = (r * r) / 8.0;
return G1_SchlickGGX(NdotV, k) * G1_SchlickGGX(NdotL, k);
}Practical Takeaway
If a material flares bright at grazing angles or the specular response looks implausible at low viewing angles, G is the first place to check. Visualize NdotV and NdotL in the shader and verify they're reaching the G term correctly. The separable Smith form used above is standard for real-time; more accurate height-correlated variants exist (Heitz, 2014) and are worth knowing for reference implementations.
© 2026 Stefan Groenewoud. All views are my own, not those of my employer.

