(y: usize, size: usize)
| 81 | } |
| 82 | |
| 83 | fn build_brdf_lut_row(y: usize, size: usize) -> Vec<u16> { |
| 84 | let n = [0.0_f32, 0.0, 1.0]; |
| 85 | let roughness = ((y as f32) + 0.5) / size as f32; |
| 86 | let mut row = Vec::with_capacity(size * 2); |
| 87 | for x in 0..size { |
| 88 | let n_dot_v = ((x as f32) + 0.5) / size as f32; |
| 89 | let v = [ |
| 90 | (1.0 - n_dot_v * n_dot_v).max(0.0).sqrt(), |
| 91 | 0.0, |
| 92 | n_dot_v, |
| 93 | ]; |
| 94 | let mut a_sum = 0.0_f32; |
| 95 | let mut b_sum = 0.0_f32; |
| 96 | for i in 0..BRDF_LUT_SAMPLES { |
| 97 | let xi = hammersley(i, BRDF_LUT_SAMPLES); |
| 98 | let h = importance_sample_ggx(xi, n, roughness); |
| 99 | let v_dot_h = dot3(v, h).max(0.0); |
| 100 | let l = [ |
| 101 | 2.0 * v_dot_h * h[0] - v[0], |
| 102 | 2.0 * v_dot_h * h[1] - v[1], |
| 103 | 2.0 * v_dot_h * h[2] - v[2], |
| 104 | ]; |
| 105 | let n_dot_l = l[2].max(0.0); |
| 106 | let n_dot_h = h[2].max(0.0); |
| 107 | if n_dot_l > 0.0 { |
| 108 | let g = geometry_smith_ggx_ibl(n_dot_v, n_dot_l, roughness); |
| 109 | let g_vis = (g * v_dot_h) / (n_dot_h * n_dot_v + 1e-6); |
| 110 | let fc = (1.0 - v_dot_h).powi(5); |
| 111 | a_sum += (1.0 - fc) * g_vis; |
| 112 | b_sum += fc * g_vis; |
| 113 | } |
| 114 | } |
| 115 | let scale = a_sum / BRDF_LUT_SAMPLES as f32; |
| 116 | let bias = b_sum / BRDF_LUT_SAMPLES as f32; |
| 117 | row.push(half::f16::from_f32(scale).to_bits()); |
| 118 | row.push(half::f16::from_f32(bias).to_bits()); |
| 119 | } |
| 120 | row |
| 121 | } |
| 122 | |
| 123 | /// Build a `size × size` BRDF LUT as packed Rg16Float texels. Each |
| 124 | /// row is constant `roughness` (v axis), each column constant `NdotV` |
no test coverage detected