GGX VNDF sample — Heitz 2018 "Sampling the GGX Distribution of Visible Normals". Gives better low-variance estimates than sampling the full distribution because we sample only the normals actually visible from the view direction.
(view_tangent: Vec3, alpha: f32, rand: Vec2)
| 1196 | /// the full distribution because we sample only the normals actually |
| 1197 | /// visible from the view direction. |
| 1198 | fn sample_ggx_vndf(view_tangent: Vec3, alpha: f32, rand: Vec2) -> Vec3 { |
| 1199 | // Transform view to hemisphere of ellipsoid. |
| 1200 | let vh = Vec3::new(alpha * view_tangent.x, alpha * view_tangent.y, view_tangent.z).normalize(); |
| 1201 | let lensq = vh.x * vh.x + vh.y * vh.y; |
| 1202 | let t1 = if lensq > 0.0 { |
| 1203 | Vec3::new(-vh.y, vh.x, 0.0) / lensq.sqrt() |
| 1204 | } else { |
| 1205 | Vec3::new(1.0, 0.0, 0.0) |
| 1206 | }; |
| 1207 | let t2 = vh.cross(t1); |
| 1208 | |
| 1209 | let r = rand.x.sqrt(); |
| 1210 | let phi = 2.0 * std::f32::consts::PI * rand.y; |
| 1211 | let t1v = r * phi.cos(); |
| 1212 | let mut t2v = r * phi.sin(); |
| 1213 | let s = 0.5 * (1.0 + vh.z); |
| 1214 | t2v = (1.0 - s) * (1.0 - t1v * t1v).max(0.0).sqrt() + s * t2v; |
| 1215 | |
| 1216 | // Build the normal, transform back to world ellipsoid. |
| 1217 | let nh = t1v * t1 + t2v * t2 + (1.0 - t1v * t1v - t2v * t2v).max(0.0).sqrt() * vh; |
| 1218 | Vec3::new(alpha * nh.x, alpha * nh.y, nh.z.max(0.0)).normalize() |
| 1219 | } |
| 1220 | |
| 1221 | struct BrdfSample { |
| 1222 | direction_world: Vec3, // outgoing/light direction in world space |