Hello,
So, I have been developing a ray-tracer... but it is slow. After some checking, I have realised that my Scene-Intersection accelerator—a BVH—is slow. I mean, much slower than the benchmarks I have seen.
Now, my intention was to implement the code in PBRT v.4 (link to function) but in Rust... I am obviously not doing so. It is accurate—i.e., the engine runs just fine—but it is too painfully slow.
Here is my function
/// Returns an `Option<Interaction>`, containing the first primitive
/// to be hit by the ray, if any
pub fn intersect(
&self,
primitives: &[Object],
ray: &mut Ray,
nodes_to_visit: &mut Vec<usize>,
) -> bool {
const MIN_T: Float = 0.0000001;
if self.nodes.is_empty() {
return false;
}
// reset
nodes_to_visit.truncate(0);
let mut prim_index: Option<usize> = None;
let mut t_squared = Float::MAX;
let inv_x = 1./ray.geometry.direction.x;
let inv_y = 1./ray.geometry.direction.y;
let inv_z = 1./ray.geometry.direction.z;
let inv_dir = Vector3D::new(inv_x, inv_y, inv_z);
let dir_is_neg = (inv_dir.x < 0., inv_dir.y < 0., inv_dir.z < 0.);
let mut current_node = 0;
for _ in 0..self.nodes.len() {
let node = &self.nodes[current_node];
if node.bounds.intersect(&ray.geometry, &inv_dir) {
if node.is_leaf() {
let offset = node.next;
// Check all the objects in this Node
for i in 0..node.n_prims {
if let Some(intersect_info) =
primitives[offset as usize + i as usize].primitive.intersect(&ray.geometry)
{
// If hit, check the distance.
let this_t_squared =
(intersect_info.p - ray.geometry.origin).length_squared();
// if the distance is less than the prevous one, update the info
if this_t_squared > MIN_T && this_t_squared < t_squared {
// If the distance is less than what we had, update return data
t_squared = this_t_squared;
prim_index = Some(offset as usize + i as usize);
ray.interaction.geometry_shading = intersect_info;
}
}
}
// update node we need to visit next, if any... otherwise, finish
if let Some(i) = nodes_to_visit.pop() {
current_node = i;
} else {
break;
}
}else{
// is interior... choose first or second child,
// add to the stack
let is_neg = match node.axis {
BBoxAxis::X => dir_is_neg.0,
BBoxAxis::Y => dir_is_neg.1,
BBoxAxis::Z => dir_is_neg.2,
};
if is_neg {
nodes_to_visit.push(current_node + 1);
current_node = node.next as usize;
} else {
nodes_to_visit.push(node.next as usize);
current_node += 1;
}
}
} else if let Some(i) = nodes_to_visit.pop() {
current_node = i;
} else {
break;
}
} // End loop
// return
if let Some(index) = prim_index {
let t = t_squared.sqrt();
ray.interaction.point = ray.geometry.project(t);
ray.interaction.wo = ray.geometry.direction * -1.;
ray.interaction.prim_index = index;
true
} else {
false
}
}
I have also checked some other references:
- Another C++ source with similar structure to PBRT/mine --> github.com/madmann91/bvh
- A Rusty implementation, but with a different structure --> github.com/svenstaro/bvh
Any suggestions as to why is this happening?
Kind regards! I am loving this forum and this community.