From 93cbb67b392922ae2e7b96e0dc95596fa2d254ee Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Tue, 5 Jul 2022 11:21:54 +0200 Subject: [PATCH] Add BVH bounding boxes missing files --- src/aabb.rs | 63 +++++++++++++++++++++++++++++++++++ src/bvh.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 src/aabb.rs create mode 100644 src/bvh.rs diff --git a/src/aabb.rs b/src/aabb.rs new file mode 100644 index 0000000..f012e03 --- /dev/null +++ b/src/aabb.rs @@ -0,0 +1,63 @@ +use crate::vec3::{Point3}; +use crate::ray::{Ray}; +use std::mem; + +#[derive(Copy, Debug, Clone)] +pub struct Aabb { + pub minimum: Point3, + pub maximum: Point3, +} + +impl Aabb { + pub fn min(&self) -> Point3 { self.minimum } + pub fn max(&self) -> Point3 { self.maximum } +} + +impl Default for Aabb { + fn default() -> Self { + Aabb {minimum: Point3::default(), maximum: Point3::default() } + } +} + +//impl Hittable for Aabb { +impl Aabb { + pub fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> bool { + let mut t_min = t_min; + let mut t_max = t_max; + for a in 0..3 { + /* + let t0 = ((self.minimum[a] - ray.origin()[a]) / ray.direction()[a]) + .min((self.maximum[a] - ray.origin()[a]) / ray.direction()[a]); + let t1 = ((self.minimum[a] - ray.origin()[a]) / ray.direction()[a]) + .max((self.maximum[a] - ray.origin()[a]) / ray.direction()[a]); + t_min = t0.min(t_min); + t_max = t1.max(t_max); + if t_max <= t_min { + return false; + }*/ + let inv_d = 1.0 / ray.direction()[a]; + let mut t0 = (self.minimum[a] - ray.origin()[a]) * inv_d; + let mut t1 = (self.maximum[a] - ray.origin()[a]) * inv_d; + if inv_d < 0.0 { + mem::swap(&mut t0, &mut t1); + } + t_min = t_min.max(t0); + t_max = t_max.min(t1); + if t_max <= t_min { + return false; + } + } + true + } + pub fn surrounding(&self, other: &Aabb) -> Aabb { + let minimum = Point3::new( + self.minimum.x().min(other.minimum.x()), + self.minimum.y().min(other.minimum.y()), + self.minimum.y().min(other.minimum.z())); + let maximum = Point3::new( + self.maximum.x().max(other.maximum.x()), + self.maximum.y().max(other.maximum.y()), + self.maximum.y().max(other.maximum.z())); + Aabb {minimum, maximum} + } +} diff --git a/src/bvh.rs b/src/bvh.rs new file mode 100644 index 0000000..0d5a46b --- /dev/null +++ b/src/bvh.rs @@ -0,0 +1,96 @@ +use crate::hittable::{HittableList, Hittable, HitRecord}; +use crate::aabb::Aabb; +use std::cmp; +use crate::Ray; + +// https://github.com/fralken/ray-tracing-the-next-week/blob/master/src/bvh.rs + +enum BVHNode { + Branch { left: Box, right: Box }, + Leaf(Box) +} + +pub struct BVH { + tree: BVHNode, + bounding_box: Aabb +} + +impl BVH { + pub fn new(mut objects: HittableList, time0: f64, time1: f64) -> Self { + fn box_compare(time0: f64, time1: f64, axis: usize) -> impl FnMut(&Box, &Box) -> cmp::Ordering { + move |a, b| { + let a_bbox = a.bounding_box(time0, time1); + let b_bbox = b.bounding_box(time0, time1); + if let (Some(a), Some(b)) = (a_bbox, b_bbox) { + let ac = a.min()[axis] + a.max()[axis]; + let bc = b.min()[axis] + b.max()[axis]; + ac.partial_cmp(&bc).unwrap() + } else { + panic!("no bounding box in bvh node") + } + } + } + + fn axis_range(objects: &HittableList, time0: f64, time1: f64, axis: usize) -> f64 { + let (min, max) = objects.iter().fold((f64::MAX, f64::MIN), |(bmin, bmax), hit| { + if let Some(aabb) = hit.bounding_box(time0, time1) { + (bmin.min(aabb.min()[axis]), bmax.max(aabb.max()[axis])) + } else { + (bmin, bmax) + } + }); + max - min + } + + let mut axis_ranges: Vec<(usize, f64)> = (0..3) + .map(|a| (a, axis_range(&objects, time0, time1, a))) + .collect(); + + axis_ranges.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let axis = axis_ranges[0].0; + + objects.sort_unstable_by(box_compare(time0, time1, axis)); + let len = objects.len(); + match len { + 0 => panic!("no elements in scene"), + 1 => { + let leaf = objects.pop().unwrap(); + if let Some(bounding_box) = leaf.bounding_box(time0, time1) { + BVH { tree: BVHNode::Leaf(leaf), bounding_box } + } else { + panic!("no bounding box in bvh node") + } + }, + _ => { + let right = BVH::new(objects.drain(len / 2..).collect(), time0, time1); + let left = BVH::new(objects, time0, time1); + let bounding_box = left.bounding_box.surrounding(&right.bounding_box); + BVH { tree: BVHNode::Branch { left: Box::new(left), right: Box::new(right) }, bounding_box } + } + } + } +} + +impl Hittable for BVH { + fn hit(&self, ray: &Ray, t_min: f64, mut t_max: f64) -> Option { + if !self.bounding_box.hit(ray, t_min, t_max) { + return None; + } + match &self.tree { + BVHNode::Leaf(leaf) => leaf.hit(ray, t_min, t_max), + BVHNode::Branch { left, right } => { + let hit_left = left.hit(ray, t_min, t_max); + if let Some(hl) = &hit_left { t_max = hl.t }; + let hit_right = right.hit(ray, t_min, t_max); + match hit_left { + Some(hit_left) => Some(hit_left), + _ => hit_right + } + } + } + } + fn bounding_box(&self, time0: f64, time1: f64) -> Option { + Some(self.bounding_box) + } +}