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};
const MAX_LEAF_TRIANGLE_COUNT: usize = 3;
#[derive(Copy, Clone, Debug)]
pub struct TriangleMesh<'a> {
time_sample_count: usize,
@ -93,7 +95,7 @@ impl<'a> TriangleMesh<'a> {
};
// 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
[(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
.traverse(rays, ray_stack, |idx_range, rays, ray_stack| {
for tri_idx in idx_range {
let tri_indices = self.indices[tri_idx];
let tri_count = idx_range.end - idx_range.start;
// For static triangles with static transforms, cache them.
let is_cached = self.time_sample_count == 1 && space.len() <= 1;
let mut tri = if is_cached {
let tri = (
// 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];
// For static triangles with static transforms, cache them.
tri_cache[i] = (
self.vertices[tri_indices.0 as usize],
self.vertices[tri_indices.1 as usize],
self.vertices[tri_indices.2 as usize],
);
if space.is_empty() {
tri
} else {
(
tri.0 * static_mat_space,
tri.1 * static_mat_space,
tri.2 * static_mat_space,
)
if !space.is_empty() {
tri_cache[i].0 = tri_cache[i].0 * static_mat_space;
tri_cache[i].1 = tri_cache[i].1 * static_mat_space;
tri_cache[i].2 = tri_cache[i].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 {
unsafe { std::mem::uninitialized() }
static_mat_space
};
// Test each ray against the current triangle.
ray_stack.do_next_task(|ray_idx| {
let ray_idx = ray_idx as usize;
let ray_time = rays.time(ray_idx);
// 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
if !is_cached {
tri = if self.time_sample_count == 1 {
let tri = if is_cached {
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.
(
self.vertices[tri_indices.0 as usize],
@ -188,29 +216,14 @@ impl<'a> Surface for TriangleMesh<'a> {
(p0, p1, p2)
};
}
// Transform triangle if necessary, and get transform space.
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
if !space.is_empty() {
tri.0 = tri.0 * mat_space;
tri.1 = tri.1 * mat_space;
tri.2 = tri.2 * mat_space;
}
} else {
// No transforms
Matrix4x4::new()
tri
};
// Test ray against triangle
@ -223,60 +236,72 @@ impl<'a> Surface for TriangleMesh<'a> {
if rays.is_occlusion(ray_idx) {
isects[ray_idx] = SurfaceIntersection::Occlude;
rays.mark_done(ray_idx);
break;
} else {
// Calculate intersection point and error magnitudes
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),
};
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
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();
});
}