Leaf triangle intersection now loops over triangles per ray.

This is the inverse of what was being done before, which was to
loop over all of the rays for each triangle.  At the moment, this
actually appears to be a tiny bit slower, but it should allow
for future optimizations testing against multiple triangles at once.
This commit is contained in:
Nathan Vegdahl 2019-07-06 09:01:24 +09:00
parent 4f7335db8c
commit 4b612e2d1a

View File

@ -14,6 +14,8 @@ use crate::{
use super::{triangle, Surface, SurfaceIntersection, SurfaceIntersectionData}; use super::{triangle, Surface, SurfaceIntersection, SurfaceIntersectionData};
const MAX_LEAF_TRIANGLE_COUNT: usize = 3;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct TriangleMesh<'a> { pub struct TriangleMesh<'a> {
time_sample_count: usize, time_sample_count: usize,
@ -93,7 +95,7 @@ impl<'a> TriangleMesh<'a> {
}; };
// Build BVH // Build BVH
let accel = BVH4::from_objects(arena, &mut indices[..], 3, |tri| { let accel = BVH4::from_objects(arena, &mut indices[..], MAX_LEAF_TRIANGLE_COUNT, |tri| {
&bounds &bounds
[(tri.3 as usize * time_sample_count)..((tri.3 as usize + 1) * time_sample_count)] [(tri.3 as usize * time_sample_count)..((tri.3 as usize + 1) * time_sample_count)]
}); });
@ -132,38 +134,64 @@ impl<'a> Surface for TriangleMesh<'a> {
self.accel self.accel
.traverse(rays, ray_stack, |idx_range, rays, ray_stack| { .traverse(rays, ray_stack, |idx_range, rays, ray_stack| {
for tri_idx in idx_range { let tri_count = idx_range.end - idx_range.start;
// Build the triangle cache if we can!
let is_cached = ray_stack.ray_count_in_next_task() >= tri_count
&& self.time_sample_count == 1
&& space.len() <= 1;
let mut tri_cache = [unsafe { std::mem::uninitialized() }; MAX_LEAF_TRIANGLE_COUNT];
if is_cached {
for tri_idx in idx_range.clone() {
let i = tri_idx - idx_range.start;
let tri_indices = self.indices[tri_idx]; let tri_indices = self.indices[tri_idx];
// For static triangles with static transforms, cache them. // For static triangles with static transforms, cache them.
let is_cached = self.time_sample_count == 1 && space.len() <= 1; tri_cache[i] = (
let mut tri = if is_cached {
let tri = (
self.vertices[tri_indices.0 as usize], self.vertices[tri_indices.0 as usize],
self.vertices[tri_indices.1 as usize], self.vertices[tri_indices.1 as usize],
self.vertices[tri_indices.2 as usize], self.vertices[tri_indices.2 as usize],
); );
if space.is_empty() { if !space.is_empty() {
tri tri_cache[i].0 = tri_cache[i].0 * static_mat_space;
} else { tri_cache[i].1 = tri_cache[i].1 * static_mat_space;
( tri_cache[i].2 = tri_cache[i].2 * static_mat_space;
tri.0 * static_mat_space, }
tri.1 * static_mat_space, }
tri.2 * static_mat_space,
)
} }
} else {
unsafe { std::mem::uninitialized() }
};
// Test each ray against the current triangle. // Test each ray against the triangles.
ray_stack.do_next_task(|ray_idx| { ray_stack.do_next_task(|ray_idx| {
let ray_idx = ray_idx as usize; let ray_idx = ray_idx as usize;
if rays.is_done(ray_idx) {
return;
}
let ray_time = rays.time(ray_idx); let ray_time = rays.time(ray_idx);
// Calculate the ray space, if necessary.
let mat_space = if space.len() > 1 {
// Per-ray transform, for motion blur
lerp_slice(space, ray_time).inverse()
} else {
static_mat_space
};
// Iterate through the triangles and test the ray against them.
let mut non_shadow_hit = false;
let mut hit_tri = unsafe { std::mem::uninitialized() };
let mut hit_tri_indices = unsafe { std::mem::uninitialized() };
let mut hit_tri_data = unsafe { std::mem::uninitialized() };
for tri_idx in idx_range.clone() {
let tri_indices = self.indices[tri_idx];
// Get triangle if necessary // Get triangle if necessary
if !is_cached { let tri = if is_cached {
tri = if self.time_sample_count == 1 { let i = tri_idx - idx_range.start;
tri_cache[i]
} else {
let mut tri = if self.time_sample_count == 1 {
// No deformation motion blur, so fast-path it. // No deformation motion blur, so fast-path it.
( (
self.vertices[tri_indices.0 as usize], self.vertices[tri_indices.0 as usize],
@ -188,29 +216,14 @@ impl<'a> Surface for TriangleMesh<'a> {
(p0, p1, p2) (p0, p1, p2)
}; };
if !space.is_empty() {
tri.0 = tri.0 * mat_space;
tri.1 = tri.1 * mat_space;
tri.2 = tri.2 * mat_space;
} }
// Transform triangle if necessary, and get transform space. tri
let mat_space = if !space.is_empty() {
if space.len() > 1 {
// Per-ray transform, for motion blur
let mat_space = lerp_slice(space, ray_time).inverse();
tri = (tri.0 * mat_space, tri.1 * mat_space, tri.2 * mat_space);
mat_space
} else {
// Same transform for all rays
if !is_cached {
tri = (
tri.0 * static_mat_space,
tri.1 * static_mat_space,
tri.2 * static_mat_space,
);
}
static_mat_space
}
} else {
// No transforms
Matrix4x4::new()
}; };
// Test ray against triangle // Test ray against triangle
@ -223,24 +236,39 @@ impl<'a> Surface for TriangleMesh<'a> {
if rays.is_occlusion(ray_idx) { if rays.is_occlusion(ray_idx) {
isects[ray_idx] = SurfaceIntersection::Occlude; isects[ray_idx] = SurfaceIntersection::Occlude;
rays.mark_done(ray_idx); rays.mark_done(ray_idx);
break;
} else { } else {
non_shadow_hit = true;
rays.set_max_t(ray_idx, t);
hit_tri = tri;
hit_tri_indices = tri_indices;
hit_tri_data = (t, b0, b1, b2);
}
}
}
// Calculate intersection data if necessary.
if non_shadow_hit {
let (t, b0, b1, b2) = hit_tri_data;
// Calculate intersection point and error magnitudes // Calculate intersection point and error magnitudes
let (pos, pos_err) = triangle::surface_point(tri, (b0, b1, b2)); let (pos, pos_err) = triangle::surface_point(hit_tri, (b0, b1, b2));
// Calculate geometric surface normal // Calculate geometric surface normal
let geo_normal = cross(tri.0 - tri.1, tri.0 - tri.2).into_normal(); let geo_normal =
cross(hit_tri.0 - hit_tri.1, hit_tri.0 - hit_tri.2).into_normal();
// Calculate interpolated surface normal, if any // Calculate interpolated surface normal, if any
let shading_normal = if let Some(normals) = self.normals { let shading_normal = if let Some(normals) = self.normals {
let n0_slice = &normals[(tri_indices.0 as usize let n0_slice = &normals[(hit_tri_indices.0 as usize
* self.time_sample_count) * self.time_sample_count)
..((tri_indices.0 as usize + 1) * self.time_sample_count)]; ..((hit_tri_indices.0 as usize + 1) * self.time_sample_count)];
let n1_slice = &normals[(tri_indices.1 as usize let n1_slice = &normals[(hit_tri_indices.1 as usize
* self.time_sample_count) * self.time_sample_count)
..((tri_indices.1 as usize + 1) * self.time_sample_count)]; ..((hit_tri_indices.1 as usize + 1) * self.time_sample_count)];
let n2_slice = &normals[(tri_indices.2 as usize let n2_slice = &normals[(hit_tri_indices.2 as usize
* self.time_sample_count) * self.time_sample_count)
..((tri_indices.2 as usize + 1) * self.time_sample_count)]; ..((hit_tri_indices.2 as usize + 1) * self.time_sample_count)];
let n0 = lerp_slice(n0_slice, ray_time).normalized(); let n0 = lerp_slice(n0_slice, ray_time).normalized();
let n1 = lerp_slice(n1_slice, ray_time).normalized(); let n1 = lerp_slice(n1_slice, ray_time).normalized();
@ -272,11 +300,8 @@ impl<'a> Surface for TriangleMesh<'a> {
intersection_data: intersection_data, intersection_data: intersection_data,
closure: shader.shade(&intersection_data, ray_time), closure: shader.shade(&intersection_data, ray_time),
}; };
rays.set_max_t(ray_idx, t);
}
} }
}); });
}
ray_stack.pop_task(); ray_stack.pop_task();
}); });
} }