Implement kd-tree.

Add simple test scene.
Failed attempt to make BVH faster.
Failed attempt to implement SAH w/ BVH.
Failed attempt to make AABB::hit faster.
This commit is contained in:
2018-09-22 11:10:09 -07:00
parent 54f1304695
commit 2104f1a76c
13 changed files with 470 additions and 61 deletions

View File

@@ -68,16 +68,112 @@ fn box_z_compare(ah: &Box<Hit>, bh: &Box<Hit>) -> std::cmp::Ordering {
}
}
// Return the first element from the vector, which should be the only element.
// TODO(wathiede): we really want a .first_into() (.first() doesn't work because it
// returns a reference.)
fn vec_first_into<T>(v: Vec<T>) -> T {
if v.len() != 1 {
panic!(format!(
"vec_first_into called for vector length != 1, length {}",
v.len()
));
}
for i in v.into_iter() {
return i;
}
panic!("Unreachable");
}
fn vec_split_into<T>(v: Vec<T>, offset: usize) -> (Vec<T>, Vec<T>) {
let mut left_half = Vec::new();
let mut right_half = Vec::new();
v.into_iter().enumerate().for_each(|(i, h)| {
if i < offset {
left_half.push(h);
} else {
right_half.push(h);
}
});
(left_half, right_half)
}
impl BVHNode {
fn new(l: Vec<Box<Hit>>, t_min: f32, t_max: f32) -> BVHNode {
if l.len() == 1 {
// Return the first element from the vector, which should be the only element.
// TODO(wathiede): we really want a .first_into() (.first() doesn't work because it
// returns a reference.)
for h in l.into_iter() {
return BVHNode::Leaf(h);
fn new_sah(l: Vec<Box<Hit>>, t_min: f32, t_max: f32) -> BVHNode {
eprintln!("l.len() {}", l.len());
let mut l: Vec<Box<Hit>> = l.into_iter().collect();
let n = l.len();
let main_box =
l[1..]
.iter()
.fold(l[0].bounding_box(t_min, t_max).unwrap(), |acc, h| {
match h.bounding_box(t_min, t_max) {
Some(new_box) => surrounding_box(&new_box, &acc),
None => panic!("hit without bounding box"),
}
});
let axis = main_box.longest_axis();
match axis {
0 => l.sort_by(box_x_compare),
1 => l.sort_by(box_y_compare),
2 => l.sort_by(box_z_compare),
val @ _ => panic!("unknown axis {}", val),
};
let boxes: Vec<_> = l
.iter()
.map(|hit| hit.bounding_box(t_min, t_max).unwrap())
.collect();
let mut left_area = Vec::with_capacity(n);
let mut left_box = boxes[0].clone();
for i in 1..n - 1 {
left_box = surrounding_box(&left_box, &boxes[i]);
left_area[i] = left_box.volume();
}
let mut right_area = Vec::with_capacity(n);
right_area[n - 1] = boxes[n - 1].volume();
let mut right_box = boxes[n - 1].clone();
for i in (0..n - 2).rev() {
right_box = surrounding_box(&right_box, &boxes[i]);
right_area[i] = right_box.volume();
}
let mut min_sah = std::f32::MAX;
let mut min_sah_idx = 0;
for i in 0..n - 1 {
let sah = i as f32 * left_area[i] + (n - i - 1) as f32 * right_area[i + 1];
if sah < min_sah {
min_sah_idx = i;
min_sah = sah;
}
panic!("Unreachable");
}
let left: Box<BVHNode>;
let right: Box<BVHNode>;
if min_sah_idx == 0 {
let (l, r) = vec_split_into(l, 1);
left = Box::new(BVHNode::Leaf(vec_first_into(l)));
right = Box::new(BVHNode::new_sah(r, t_min, t_max));
} else if min_sah_idx == n - 2 {
let (l, r) = vec_split_into(l, min_sah_idx + 1);
left = Box::new(BVHNode::new_sah(l, t_min, t_max));
right = Box::new(BVHNode::Leaf(vec_first_into(r)));
} else {
let (l, r) = vec_split_into(l, min_sah_idx + 1);
left = Box::new(BVHNode::new_sah(l, t_min, t_max));
right = Box::new(BVHNode::new_sah(r, t_min, t_max));
}
BVHNode::Branch {
left,
right,
bbox: Some(main_box),
}
}
fn new_dumb(l: Vec<Box<Hit>>, t_min: f32, t_max: f32) -> BVHNode {
if l.len() == 1 {
return BVHNode::Leaf(vec_first_into(l));
} else {
let mut l: Vec<Box<Hit>> = l.into_iter().collect();
let mut rng = rand::thread_rng();
@@ -88,18 +184,10 @@ impl BVHNode {
val @ _ => panic!("unknown axis {}", val),
}
let mut left_half = Vec::new();
let mut right_half = Vec::new();
let half_idx = l.len() / 2;
l.into_iter().enumerate().for_each(|(i, h)| {
if i < half_idx {
left_half.push(h);
} else {
right_half.push(h);
}
});
let left = Box::new(BVHNode::new(left_half, t_min, t_max));
let right = Box::new(BVHNode::new(right_half, t_min, t_max));
let half = l.len() / 2;
let (left_half, right_half) = vec_split_into(l, half);
let left = Box::new(BVHNode::new_dumb(left_half, t_min, t_max));
let right = Box::new(BVHNode::new_dumb(right_half, t_min, t_max));
let bbox = BVHNode::surrounding_box(left.as_ref(), right.as_ref(), t_min, t_max);
return BVHNode::Branch { left, right, bbox };
}
@@ -129,6 +217,7 @@ impl Hit for BVHNode {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
match self {
BVHNode::Leaf(ref hit) => hit.hit(r, t_min, t_max),
// TODO(wathiede): call bbox.hit() before recursing down both paths of tree.
BVHNode::Branch {
ref left,
ref right,
@@ -163,7 +252,7 @@ impl BVH {
let count = l.len();
let start = Instant::now();
let bvh = BVH {
root: BVHNode::new(l, t_min, t_max),
root: BVHNode::new_sah(l, t_min, t_max),
};
let runtime = start.elapsed();
info!(