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:
131
rtiow/src/bvh.rs
131
rtiow/src/bvh.rs
@@ -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!(
|
||||
|
||||
Reference in New Issue
Block a user