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;
let tri_indices = self.indices[tri_idx];
// For static triangles with static transforms, cache them. // Build the triangle cache if we can!
let is_cached = self.time_sample_count == 1 && space.len() <= 1; let is_cached = ray_stack.ray_count_in_next_task() >= tri_count
let mut tri = if is_cached { && self.time_sample_count == 1
let tri = ( && 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];
// For static triangles with static transforms, cache them.
tri_cache[i] = (
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,
)
} }
}
}
// Test each ray against the triangles.
ray_stack.do_next_task(|ray_idx| {
let ray_idx = ray_idx as usize;
if rays.is_done(ray_idx) {
return;
}
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 { } else {
unsafe { std::mem::uninitialized() } static_mat_space
}; };
// Test each ray against the current triangle. // Iterate through the triangles and test the ray against them.
ray_stack.do_next_task(|ray_idx| { let mut non_shadow_hit = false;
let ray_idx = ray_idx as usize; let mut hit_tri = unsafe { std::mem::uninitialized() };
let ray_time = rays.time(ray_idx); 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)
}; };
}
// Transform triangle if necessary, and get transform space. if !space.is_empty() {
let mat_space = if !space.is_empty() { tri.0 = tri.0 * mat_space;
if space.len() > 1 { tri.1 = tri.1 * mat_space;
// Per-ray transform, for motion blur tri.2 = tri.2 * mat_space;
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 tri
Matrix4x4::new()
}; };
// Test ray against triangle // Test ray against triangle
@ -223,60 +236,72 @@ 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 {
// Calculate intersection point and error magnitudes non_shadow_hit = true;
let (pos, pos_err) = triangle::surface_point(tri, (b0, b1, b2));
// Calculate geometric surface normal
let geo_normal = cross(tri.0 - tri.1, tri.0 - tri.2).into_normal();
// Calculate interpolated surface normal, if any
let shading_normal = if let Some(normals) = self.normals {
let n0_slice = &normals[(tri_indices.0 as usize
* self.time_sample_count)
..((tri_indices.0 as usize + 1) * self.time_sample_count)];
let n1_slice = &normals[(tri_indices.1 as usize
* self.time_sample_count)
..((tri_indices.1 as usize + 1) * self.time_sample_count)];
let n2_slice = &normals[(tri_indices.2 as usize
* self.time_sample_count)
..((tri_indices.2 as usize + 1) * self.time_sample_count)];
let n0 = lerp_slice(n0_slice, ray_time).normalized();
let n1 = lerp_slice(n1_slice, ray_time).normalized();
let n2 = lerp_slice(n2_slice, ray_time).normalized();
let s_nor = ((n0 * b0) + (n1 * b1) + (n2 * b2)) * mat_space;
if dot(s_nor, geo_normal) >= 0.0 {
s_nor
} else {
-s_nor
}
} else {
geo_normal
};
let intersection_data = SurfaceIntersectionData {
incoming: rays.dir(ray_idx),
t: t,
pos: pos,
pos_err: pos_err,
nor: shading_normal,
nor_g: geo_normal,
local_space: mat_space,
sample_pdf: 0.0,
};
// Fill in intersection data
isects[ray_idx] = SurfaceIntersection::Hit {
intersection_data: intersection_data,
closure: shader.shade(&intersection_data, ray_time),
};
rays.set_max_t(ray_idx, t); 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
let (pos, pos_err) = triangle::surface_point(hit_tri, (b0, b1, b2));
// Calculate geometric surface 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
let shading_normal = if let Some(normals) = self.normals {
let n0_slice = &normals[(hit_tri_indices.0 as usize
* self.time_sample_count)
..((hit_tri_indices.0 as usize + 1) * self.time_sample_count)];
let n1_slice = &normals[(hit_tri_indices.1 as usize
* self.time_sample_count)
..((hit_tri_indices.1 as usize + 1) * self.time_sample_count)];
let n2_slice = &normals[(hit_tri_indices.2 as usize
* 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 n1 = lerp_slice(n1_slice, ray_time).normalized();
let n2 = lerp_slice(n2_slice, ray_time).normalized();
let s_nor = ((n0 * b0) + (n1 * b1) + (n2 * b2)) * mat_space;
if dot(s_nor, geo_normal) >= 0.0 {
s_nor
} else {
-s_nor
}
} else {
geo_normal
};
let intersection_data = SurfaceIntersectionData {
incoming: rays.dir(ray_idx),
t: t,
pos: pos,
pos_err: pos_err,
nor: shading_normal,
nor_g: geo_normal,
local_space: mat_space,
sample_pdf: 0.0,
};
// Fill in intersection data
isects[ray_idx] = SurfaceIntersection::Hit {
intersection_data: intersection_data,
closure: shader.shade(&intersection_data, ray_time),
};
}
});
ray_stack.pop_task(); ray_stack.pop_task();
}); });
} }