1
0

Completed AABB BVH implementation

This commit is contained in:
Ishan Jain 2021-03-02 01:25:27 +05:30
parent 5916155b8e
commit 18bb6a250b
No known key found for this signature in database
GPG Key ID: F261A0E73038D89D
9 changed files with 253 additions and 15 deletions

51
src/aabb.rs Normal file
View File

@ -0,0 +1,51 @@
use crate::types::{Ray, Vec3};
#[derive(Debug, Copy, Clone)]
pub struct Aabb {
pub min: Vec3,
pub max: Vec3,
}
impl Aabb {
pub const fn new(min: Vec3, max: Vec3) -> Self {
Self { min, max }
}
pub fn hit(&self, ray: &Ray, mut t_min: f64, mut t_max: f64) -> bool {
for i in 0..=2 {
let inverse_dir = 1.0 / ray.direction()[i];
let mut t0 = (self.min[i] - ray.origin()[i]) * inverse_dir;
let mut t1 = (self.max[i] - ray.origin()[i]) * inverse_dir;
if inverse_dir < 0.0 {
std::mem::swap(&mut t0, &mut t1);
}
t_min = if t0 > t_min { t0 } else { t_min };
t_max = if t1 < t_max { t1 } else { t_max };
if t_max <= t_min {
return false;
}
}
true
}
pub fn surrounding_box(box0: Aabb, box1: Aabb) -> Self {
let smol_box = Vec3::new(
box0.min.x().min(box1.min.x()),
box0.min.y().min(box1.min.y()),
box0.min.z().min(box1.min.z()),
);
let big_box = Vec3::new(
box0.max.x().max(box1.max.x()),
box0.max.y().max(box1.max.y()),
box0.max.z().max(box1.max.z()),
);
Self {
min: smol_box,
max: big_box,
}
}
}

134
src/bvh.rs Normal file
View File

@ -0,0 +1,134 @@
use std::cmp::Ordering;
use rand::{prelude::SliceRandom, Rng};
use crate::{types::Ray, Aabb, HitRecord, Hitable};
pub struct BvhNode<T: Hitable> {
bounding_box: Aabb,
left: HitNode<T>,
right: HitNode<T>,
}
impl<T: Hitable + Clone> BvhNode<T> {
pub fn new<R: Rng + ?Sized>(rng: &mut R, objects: &mut [T], t0: f64, t1: f64) -> Self {
let comparator = [
Self::box_x_compare,
Self::box_y_compare,
Self::box_z_compare,
]
.choose(rng)
.unwrap();
objects.sort_by(comparator);
let (left, right) = match objects.len() {
1 => (
HitNode::Direct(objects[0].clone()),
HitNode::Direct(objects[0].clone()),
),
2 => match comparator(&objects[0], &objects[1]) {
Ordering::Greater => (
HitNode::Direct(objects[1].clone()),
HitNode::Direct(objects[0].clone()),
),
_ => (
HitNode::Direct(objects[0].clone()),
HitNode::Direct(objects[1].clone()),
),
},
n => {
let (l, r) = objects.split_at_mut(n / 2);
(
HitNode::Bvh(Box::new(BvhNode::new(rng, l, t0, t1))),
HitNode::Bvh(Box::new(BvhNode::new(rng, r, t0, t1))),
)
}
};
let left_box = left
.bounding_box(t0, t1)
.expect("missing bounding box for left BVH Node");
let right_box = right
.bounding_box(t0, t1)
.expect("missing bounding box for right BVH Node");
Self {
left,
right,
bounding_box: Aabb::surrounding_box(left_box, right_box),
}
}
fn box_x_compare(obj1: &T, obj2: &T) -> Ordering {
if let (Some(bbox_a), Some(bbox_b)) =
(obj1.bounding_box(0.0, 0.0), obj2.bounding_box(0.0, 0.0))
{
return bbox_a.min.x().partial_cmp(&bbox_b.min.x()).unwrap();
}
panic!("No bounding box for this BVH Node!!")
}
fn box_y_compare(obj1: &T, obj2: &T) -> Ordering {
if let (Some(bbox_a), Some(bbox_b)) =
(obj1.bounding_box(0.0, 0.0), obj2.bounding_box(0.0, 0.0))
{
return bbox_a.min.y().partial_cmp(&bbox_b.min.y()).unwrap();
}
panic!("No bounding box for this BVH Node!!")
}
fn box_z_compare(obj1: &T, obj2: &T) -> Ordering {
if let (Some(bbox_a), Some(bbox_b)) =
(obj1.bounding_box(0.0, 0.0), obj2.bounding_box(0.0, 0.0))
{
return bbox_a.min.z().partial_cmp(&bbox_b.min.z()).unwrap();
}
panic!("No bounding box for this BVH Node!!")
}
}
impl<T: Hitable> Hitable for BvhNode<T> {
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
self.bounding_box(t_min, t_max)?;
let hbox_left = self.left.hit(ray, t_min, t_max);
let hbox_right = if let Some(ref hleft) = hbox_left {
self.right.hit(ray, t_min, hleft.t)
} else {
self.right.hit(ray, t_min, t_max)
};
hbox_right.or(hbox_left)
}
fn bounding_box(&self, _t_min: f64, _t_max: f64) -> Option<Aabb> {
Some(self.bounding_box)
}
}
enum HitNode<T: Hitable> {
Bvh(Box<BvhNode<T>>),
Direct(T),
}
impl<T: Hitable> HitNode<T> {
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
match self {
HitNode::Bvh(node) => node.hit(ray, t_min, t_max),
HitNode::Direct(node) => node.hit(ray, t_min, t_max),
}
}
fn bounding_box(&self, t0: f64, t1: f64) -> Option<Aabb> {
match self {
HitNode::Bvh(node) => node.bounding_box(t0, t1),
HitNode::Direct(node) => node.bounding_box(t0, t1),
}
}
}

View File

@ -1,10 +1,12 @@
use crate::BvhNode;
use { use {
crate::{ crate::{
types::{ types::{
material::{Dielectric, Lambertian, Metal}, material::{Dielectric, Lambertian, Metal},
MovingSphere, Ray, Sphere, Vec3, MovingSphere, Ray, Sphere, Vec3,
}, },
Camera, Hitable, HitableList, HORIZONTAL_PARTITION, VERTICAL_PARTITION, Camera, Hitable, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
}, },
rand::{rngs::SmallRng, Rng, SeedableRng}, rand::{rngs::SmallRng, Rng, SeedableRng},
rayon::prelude::*, rayon::prelude::*,
@ -49,9 +51,7 @@ impl Demo {
} }
fn world(&self) -> impl Hitable { fn world(&self) -> impl Hitable {
let mut world = HitableList { let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(500);
list: Vec::with_capacity(500),
};
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let mut rng = SmallRng::from_rng(&mut rng).unwrap(); let mut rng = SmallRng::from_rng(&mut rng).unwrap();
@ -125,7 +125,7 @@ impl Demo {
Metal::with_fuzz(Vec3::new(0.7, 0.6, 0.5), 0.0), Metal::with_fuzz(Vec3::new(0.7, 0.6, 0.5), 0.0),
))); )));
world BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
} }
fn camera(&self, aspect_ratio: f64) -> Camera { fn camera(&self, aspect_ratio: f64) -> Camera {

View File

@ -1,4 +1,9 @@
use crate::types::{Material, Ray, Vec3}; use std::sync::Arc;
use crate::{
types::{Material, Ray, Vec3},
Aabb,
};
pub struct HitRecord<'a> { pub struct HitRecord<'a> {
/// Rays are represented by A + t * B /// Rays are represented by A + t * B
@ -22,7 +27,16 @@ pub struct HitRecord<'a> {
} }
pub trait Hitable { pub trait Hitable {
fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord> { fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord>;
None
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb>;
}
impl<T: Hitable + ?Sized> Hitable for Arc<T> {
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
self.as_ref().hit(ray, t_min, t_max)
}
fn bounding_box(&self, t0: f64, t1: f64) -> Option<Aabb> {
self.as_ref().bounding_box(t0, t1)
} }
} }

View File

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{demo::ParallelHit, types::Ray, HitRecord, Hitable}; use crate::{demo::ParallelHit, types::Ray, Aabb, HitRecord, Hitable};
pub struct HitableList { pub struct HitableList {
pub list: Vec<Arc<dyn ParallelHit>>, pub list: Vec<Arc<dyn ParallelHit>>,
@ -18,6 +18,28 @@ impl Hitable for HitableList {
} }
hit_rec hit_rec
} }
fn bounding_box(&self, t0: f64, t1: f64) -> Option<Aabb> {
if self.list.is_empty() {
return None;
}
let mut output_box = None;
for obj in self.list.iter() {
if let Some(bbox) = obj.bounding_box(t0, t1) {
if let Some(ref mut opbox) = output_box {
*opbox = Aabb::surrounding_box(*opbox, bbox);
} else {
output_box = Some(bbox);
}
} else {
return output_box;
}
}
output_box
}
} }
impl HitableList { impl HitableList {

View File

@ -1,5 +1,7 @@
#![allow(clippy::suspicious_arithmetic_impl)] #![allow(clippy::suspicious_arithmetic_impl)]
mod aabb;
mod bvh;
mod camera; mod camera;
mod demo; mod demo;
mod hitable; mod hitable;
@ -8,13 +10,15 @@ mod types;
pub use camera::Camera; pub use camera::Camera;
pub use aabb::Aabb;
pub use bvh::BvhNode;
pub use hitable::{HitRecord, Hitable}; pub use hitable::{HitRecord, Hitable};
pub use hitable_list::HitableList; pub use hitable_list::HitableList;
use std::time::Instant; use std::time::Instant;
const NUM_SAMPLES: u8 = 5; const NUM_SAMPLES: u8 = 25;
const VERTICAL_PARTITION: usize = 8; const VERTICAL_PARTITION: usize = 12;
const HORIZONTAL_PARTITION: usize = 8; const HORIZONTAL_PARTITION: usize = 12;
const WIDTH: usize = 1920; const WIDTH: usize = 1920;
const HEIGHT: usize = 1080; const HEIGHT: usize = 1080;

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
types::{Material, Ray, Vec3}, types::{Material, Ray, Vec3},
HitRecord, Hitable, Aabb, HitRecord, Hitable,
}; };
pub struct MovingSphere<T: Material + Sized> { pub struct MovingSphere<T: Material + Sized> {
@ -72,4 +72,12 @@ impl<T: Material + Sized> Hitable for MovingSphere<T> {
} }
None None
} }
fn bounding_box(&self, t0: f64, t1: f64) -> Option<Aabb> {
let radius = Vec3::new(self.radius, self.radius, self.radius);
let box_smol = Aabb::new(self.center(t0) - radius, self.center(t0) + radius);
let box_big = Aabb::new(self.center(t1) - radius, self.center(t1) + radius);
Some(Aabb::surrounding_box(box_smol, box_big))
}
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
types::{Material, Ray, Vec3}, types::{Material, Ray, Vec3},
HitRecord, Hitable, Aabb, HitRecord, Hitable,
}; };
pub struct Sphere<T: Material + Sized> { pub struct Sphere<T: Material + Sized> {
@ -61,4 +61,9 @@ impl<T: Material + Sized> Hitable for Sphere<T> {
} }
None None
} }
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb> {
let radius = Vec3::new(self.radius, self.radius, self.radius);
Some(Aabb::new(self.center - radius, self.center + radius))
}
} }

View File

@ -3,7 +3,7 @@ use std::{
ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign}, ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign},
}; };
#[derive(Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct Vec3([f64; 3]); pub struct Vec3([f64; 3]);
impl Vec3 { impl Vec3 {