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

Function surface_from_hit

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

Build the per-pixel surface sample from a BVH hit. Interpolates barycentric attributes, samples material textures, and perturbs the shading normal via the tangent-space normal map (if present).

(scene: &Scene, ray: &Ray, hit: &Hit)

Source from the content-addressed store, hash-verified

1118/// barycentric attributes, samples material textures, and perturbs the
1119/// shading normal via the tangent-space normal map (if present).
1120fn surface_from_hit(scene: &Scene, ray: &Ray, hit: &Hit) -> SurfaceSample {
1121 let tri = &scene.triangles[hit.triangle_index as usize];
1122 let u = hit.barycentric.x;
1123 let v = hit.barycentric.y;
1124 let w = 1.0 - u - v;
1125
1126 let geom_normal = (tri.n0 * w + tri.n1 * u + tri.n2 * v).normalize_or_zero();
1127 let uv = tri.uv0 * w + tri.uv1 * u + tri.uv2 * v;
1128 let position = ray.origin + ray.direction * hit.t;
1129
1130 let material = &scene.materials[tri.material_index as usize];
1131 let base_color = scene.sample_base_color(material, uv);
1132 let (metallic, roughness) = scene.sample_metallic_roughness(material, uv);
1133 let emissive = scene.sample_emissive(material, uv);
1134 let occlusion = scene.sample_occlusion(material, uv);
1135
1136 // Build the per-hit TBN from the interpolated tangent+normal. The
1137 // bitangent sign comes from the glTF tangent.w (±1) — if the mesh
1138 // has no tangents (length 0), skip normal mapping entirely.
1139 let tangent_interp = tri.t0 * w + tri.t1 * u + tri.t2 * v;
1140 let tangent_xyz = Vec3::new(tangent_interp.x, tangent_interp.y, tangent_interp.z);
1141 let shading_normal = if material.normal_texture.is_some()
1142 && tangent_xyz.length_squared() > 1e-8
1143 {
1144 let t = tangent_xyz.normalize();
1145 // Re-orthogonalize the tangent against the normal (Gram-Schmidt)
1146 // so numerical drift from interpolation doesn't skew the basis.
1147 let t = (t - geom_normal * geom_normal.dot(t)).normalize_or_zero();
1148 let bitangent_sign = tangent_interp.w.signum().max(-1.0).min(1.0);
1149 let b = geom_normal.cross(t) * bitangent_sign;
1150
1151 let n_tangent = scene.sample_tangent_normal(material, uv);
1152 // Compose tangent-space normal into world space.
1153 (t * n_tangent.x + b * n_tangent.y + geom_normal * n_tangent.z).normalize_or_zero()
1154 } else {
1155 geom_normal
1156 };
1157
1158 SurfaceSample {
1159 position,
1160 normal: shading_normal,
1161 base_color,
1162 metallic,
1163 roughness: roughness.max(0.04), // clamp to avoid div-by-zero
1164 emissive,
1165 occlusion,
1166 }
1167}
1168
1169/// Build an orthonormal basis (tangent, bitangent) around `n`.
1170/// Branchless method from Frisvad 2012 — more stable than cross(n, up)

Callers 1

trace_pathFunction · 0.85

Calls 5

sample_base_colorMethod · 0.80
sample_emissiveMethod · 0.80
sample_occlusionMethod · 0.80
sample_tangent_normalMethod · 0.80

Tested by

no test coverage detected