MCPcopy Create free account
hub / github.com/Bloom-Engine/engine / evaluate_brdf

Function evaluate_brdf

tools/bloom-reference/src/main.rs:1719–1764  ·  view source on GitHub ↗

Evaluate the BRDF at a surface for a given outgoing (toward-eye) and incoming (toward-light) direction pair. Returns (brdf * N·L) which is what the light-transport equation actually needs, plus the PDF the BRDF sampler would have chosen for this incoming direction — used by MIS to weight NEE vs BRDF samples.

(surface: &SurfaceSample, view: Vec3, light: Vec3)

Source from the content-addressed store, hash-verified

1717/// the PDF the BRDF sampler would have chosen for this incoming
1718/// direction — used by MIS to weight NEE vs BRDF samples.
1719fn evaluate_brdf(surface: &SurfaceSample, view: Vec3, light: Vec3) -> (Vec3, f32) {
1720 let n = surface.normal;
1721 let n_dot_v = n.dot(view).max(0.0);
1722 let n_dot_l = n.dot(light).max(0.0);
1723 if n_dot_l <= 0.0 || n_dot_v <= 0.0 {
1724 return (Vec3::ZERO, 0.0);
1725 }
1726 let h = (view + light).normalize_or_zero();
1727 let n_dot_h = n.dot(h).max(0.0);
1728 let v_dot_h = view.dot(h).max(0.0);
1729
1730 let f0 = Vec3::splat(0.04).lerp(surface.base_color, surface.metallic);
1731 let alpha = surface.roughness * surface.roughness;
1732
1733 // Specular: F * D * Vsmith. Vsmith is the height-correlated form
1734 // that already includes the 1/(4·N·V·N·L) term.
1735 let f = fresnel_schlick(v_dot_h, f0);
1736 let d = d_ggx(n_dot_h, alpha);
1737 let vsmith = v_smith(n_dot_v, n_dot_l, alpha);
1738 let specular = f * d * vsmith;
1739
1740 // Diffuse: Burley (already 1/pi-normalized). Scale by (1 - F) and
1741 // (1 - metallic) for energy conservation.
1742 let fd = burley_diffuse(n_dot_l, n_dot_v, v_dot_h, surface.roughness);
1743 let diffuse_albedo = surface.base_color * (1.0 - surface.metallic) * (Vec3::ONE - f);
1744 let diffuse = diffuse_albedo * fd;
1745
1746 let brdf_cos = (specular + diffuse) * n_dot_l;
1747
1748 // Rough approximation of the BRDF sampler's PDF for MIS. Uses the
1749 // same spec/diff split heuristic as `sample_brdf`.
1750 let spec_weight = (f0.x + f0.y + f0.z) / 3.0;
1751 let diff_weight = (1.0 - spec_weight) * (1.0 - surface.metallic);
1752 let total = spec_weight + diff_weight + 1e-6;
1753 let p_spec = spec_weight / total;
1754 let p_diff = 1.0 - p_spec;
1755
1756 // Spec PDF (GGX VNDF): D * G1(V) * max(0, V·H) / (4 * N·V * V·H).
1757 // We just approximate D·cos/(4·V·H) since we only need a
1758 // reasonable ratio for MIS — exact matching isn't required.
1759 let pdf_spec = d * n_dot_h / (4.0 * v_dot_h + 1e-6);
1760 let pdf_diff = n_dot_l / std::f32::consts::PI;
1761 let pdf = p_spec * pdf_spec + p_diff * pdf_diff;
1762
1763 (brdf_cos, pdf.max(0.0))
1764}
1765
1766/// Balance heuristic for MIS — weights a sample from strategy A by
1767/// p_a / (p_a + p_b). Standard in PBRT / Eric Veach's thesis.

Callers 1

trace_pathFunction · 0.85

Calls 4

fresnel_schlickFunction · 0.85
d_ggxFunction · 0.85
v_smithFunction · 0.85
burley_diffuseFunction · 0.85

Tested by

no test coverage detected