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

The Microfacet BRDF Formula

  • 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

F — Fresnel Term


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