-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
improvement: BVHNode hit method optimization
earlier returns based on the distances to the child nodes' axis-aligned bounding boxes. rays no longer need to traverse all the way through objects to return the closest hitpoint
- Loading branch information
Showing
5 changed files
with
137 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,110 @@ | ||
use crate::{hitable::Hitable, ray::Ray, Float}; | ||
use rand::rngs::SmallRng; | ||
|
||
use crate::{ | ||
hitable::{HitRecord, Hitable, HitableTrait}, | ||
ray::Ray, | ||
Float, | ||
}; | ||
|
||
use super::BVHNode; | ||
|
||
impl<'scene> BVHNode<'scene> { | ||
// NOTE: this must be kept in close alignment with the implementation of BVHNode::hit()! | ||
// TODO: maybe move the statistics counting to the method itself? Measure the impact? | ||
/// Alternate hit method that maintains a test count for the BVH traversals. | ||
pub fn bvh_testcount( | ||
&'scene self, | ||
depth: &mut usize, | ||
ray: &Ray, | ||
distance_min: Float, | ||
distance_max: Float, | ||
) { | ||
rng: &mut SmallRng, | ||
) -> Option<HitRecord> { | ||
*depth += 1; | ||
|
||
// If we do not hit the bounding box of current node, early return None | ||
if !self.bounding_box.hit(ray, distance_min, distance_max) { | ||
return; | ||
return None; | ||
} | ||
|
||
Self::bvh_testcount_recurse_condition(&self.left, depth, ray, distance_min, distance_max); | ||
Self::bvh_testcount_recurse_condition(&self.right, depth, ray, distance_min, distance_max); | ||
} | ||
// Check the distance to the bounding boxes | ||
let (left_aabb_distance, right_aabb_distance) = | ||
match (self.left.bounding_box(), self.right.bounding_box()) { | ||
// Early returns, if there's no bounding box | ||
(None, None) => return None, | ||
(Some(_l), None) => { | ||
return recurse(&self.left, depth, ray, distance_min, distance_max, rng) | ||
} | ||
(None, Some(_r)) => { | ||
return recurse(&self.right, depth, ray, distance_min, distance_max, rng) | ||
} | ||
// If we have bounding boxes, get the distances | ||
(Some(l), Some(r)) => (l.distance(ray), r.distance(ray)), | ||
}; | ||
let (_closest_aabb_distance, furthest_aabb_distance) = | ||
match (left_aabb_distance, right_aabb_distance) { | ||
// Early return: neither child AABB can be hit with the ray | ||
(None, None) => return None, | ||
// Early return: only one child can be hit with the ray | ||
(Some(_d), None) => { | ||
return recurse(&self.left, depth, ray, distance_min, distance_max, rng) | ||
} | ||
(None, Some(_d)) => { | ||
return recurse(&self.right, depth, ray, distance_min, distance_max, rng) | ||
} | ||
// Default case: both children can be hit with the ray, check the distance | ||
(Some(l), Some(r)) => (Float::min(l, r), Float::max(l, r)), | ||
}; | ||
|
||
fn bvh_testcount_recurse_condition( | ||
bvhnode: &'scene Hitable, // BVHNode | ||
depth: &mut usize, | ||
ray: &Ray, | ||
distance_min: Float, | ||
distance_max: Float, | ||
) { | ||
match bvhnode { | ||
Hitable::BVHNode(bvh) => bvh.bvh_testcount(depth, ray, distance_min, distance_max), | ||
Hitable::STL(s) => s | ||
.bvhnode | ||
.bvh_testcount(depth, ray, distance_min, distance_max), | ||
Hitable::GLTF(g) => g | ||
.bvhnode | ||
.bvh_testcount(depth, ray, distance_min, distance_max), | ||
_ => (), | ||
// Check the closest first | ||
let (closest_bvh, furthest_bvh) = if left_aabb_distance < right_aabb_distance { | ||
(&self.left, &self.right) | ||
} else { | ||
(&self.right, &self.left) | ||
}; | ||
let closest_bvh_hit = recurse(closest_bvh, depth, ray, distance_min, distance_max, rng); | ||
|
||
// Is the hit closer than the closest point of the other AABB? | ||
if let Some(ref hit_record) = closest_bvh_hit { | ||
if hit_record.distance < furthest_aabb_distance { | ||
return Some(hit_record.clone()); | ||
} | ||
} | ||
// Otherwise, check the other child too | ||
let furthest_bvh_hit = recurse(furthest_bvh, depth, ray, distance_min, distance_max, rng); | ||
|
||
// Did we hit neither of the child nodes, one of them, or both? | ||
// Return the closest thing we hit | ||
match (&closest_bvh_hit, &furthest_bvh_hit) { | ||
(None, None) => None, | ||
(None, Some(_)) => furthest_bvh_hit, | ||
(Some(_), None) => closest_bvh_hit, | ||
(Some(left), Some(right)) => { | ||
if left.distance < right.distance { | ||
return closest_bvh_hit; | ||
} | ||
furthest_bvh_hit | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn recurse<'scene>( | ||
bvhnode: &'scene Hitable, // BVHNode | ||
depth: &mut usize, | ||
ray: &Ray, | ||
distance_min: Float, | ||
distance_max: Float, | ||
rng: &mut SmallRng, | ||
) -> Option<HitRecord<'scene>> { | ||
match bvhnode { | ||
Hitable::BVHNode(bvh) => bvh.bvh_testcount(depth, ray, distance_min, distance_max, rng), | ||
Hitable::STL(s) => s | ||
.bvhnode | ||
.bvh_testcount(depth, ray, distance_min, distance_max, rng), | ||
Hitable::GLTF(g) => g | ||
.bvhnode | ||
.bvh_testcount(depth, ray, distance_min, distance_max, rng), | ||
hitable => hitable.hit(ray, distance_min, distance_max, rng), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters