-
Concept: Microfacet Theory
-
The Core Idea
- Real surfaces are not perfectly smooth — they have microscopic bumps (microfacets)
- Each microfacet is a perfect mirror (specular reflector)
- The macroscopic BRDF is the statistical aggregate of all microfacet reflections
- Roughness controls the spread of microfacet normals
-
f_r(ω_i, ω_o) = D(h) * G(ω_i, ω_o) * F(ω_o, h) / (4 * NdotL * NdotV)
h = normalize(ω_i + ω_o) — half-vector (microfacet normal that reflects ω_i to ω_o)
- Three components: NDF, Geometry, Fresnel
-
D — Normal Distribution Function (NDF)
- Describes the statistical distribution of microfacet normals
D(h) = density of microfacets with normal h (per steradian)
- Must satisfy:
∫_Ω D(h) cos(θ_h) dω_h = 1
- GGX (Trowbridge-Reitz) NDF
D(h) = α² / (π * (NdotH² * (α² - 1) + 1)²)
α = roughness² — perceptual roughness remapping
α = 0 → perfect mirror (delta distribution), α = 1 → fully rough
- Why
roughness²? Perceptual linearity — equal steps in roughness look equal
- GGX has heavier tails than Beckmann — better matches real surfaces
- Beckmann NDF (older, used in Cook-Torrance)
D(h) = exp(-tan²(θ_h) / α²) / (π * α² * cos⁴(θ_h))
- Lighter tails than GGX — less realistic for rough surfaces
- Anisotropic GGX
- Different roughness along tangent and bitangent directions
D(h) = 1 / (π * α_x * α_y * (NdotH/α_x)² + (TdotH/α_x)² + (BdotH/α_y)²)²
- Used for brushed metal, hair, fabric
-
G — Geometric Attenuation (Shadowing-Masking)
- Accounts for microfacets blocking each other
- Shadowing: incoming light blocked by other microfacets
- Masking: outgoing light blocked by other microfacets
- Smith G term (separable approximation)
G(ω_i, ω_o) = G1(ω_i) * G1(ω_o)
G1(ω) = NdotV / (NdotV * (1 - k) + k)
- For direct lighting:
k = (roughness + 1)² / 8
- For IBL:
k = roughness² / 2
- Height-correlated Smith (more accurate)
G(ω_i, ω_o) = 1 / (1 + Λ(ω_i) + Λ(ω_o))
Λ(ω) = (-1 + sqrt(1 + α² * tan²(θ))) / 2
- Accounts for correlation between shadowing and masking
- Why the
4 * NdotL * NdotV denominator?
- Jacobian of the half-vector transform:
dω_h / dω_i = 1 / (4 * dot(ω_o, h))
- Combined with the NdotL and NdotV from the rendering equation
-
Energy Conservation
- The microfacet BRDF is not perfectly energy-conserving
- Multiple scattering between microfacets is ignored
- At high roughness: energy is lost (surface appears too dark)
- Fix: multi-scattering BRDF (Heitz et al. 2016)
- Add a compensation term for energy lost to multiple bounces
f_ms = (1 - E(ω_o)) * (1 - E(ω_i)) / (π * (1 - E_avg))
E(ω) = directional albedo (precomputed LUT)
-
Roughness Remapping
- Artists work with “perceptual roughness”
r ∈ [0,1]
α = r² — squaring gives more intuitive control
- At
r = 0.5: α = 0.25 — medium roughness
- Without remapping: most of the interesting range is compressed near 0
- Some engines use
α = r directly — check which convention your engine uses
-
Sampling the GGX NDF
- To importance sample GGX, sample the half-vector
h from D(h) * cos(θ_h)
- Spherical coordinates:
θ_h = arctan(α * sqrt(ξ₁ / (1 - ξ₁)))
φ_h = 2π * ξ₂
- Convert to Cartesian (in tangent space):
h = (sin(θ_h)*cos(φ_h), sin(θ_h)*sin(φ_h), cos(θ_h))
- Transform to world space using TBN matrix
- Reflect view direction:
ω_i = reflect(-ω_o, h)
- PDF:
p(ω_i) = D(h) * NdotH / (4 * VdotH)
- Visible NDF sampling (VNDF) — Heitz 2018
- Sample proportional to
D(h) * G1(ω_o) * max(0, dot(ω_o, h))
- Better importance sampling — fewer wasted samples for grazing angles
- Significantly reduces variance for rough surfaces viewed at grazing angles