rtiow: BVHTriangles use binning to speed up BVH building.

This commit is contained in:
Bill Thiede 2023-02-12 16:52:07 -08:00
parent 7d9750b9d0
commit 1076e6dcaf

View File

@ -59,6 +59,12 @@ where
bvh_nodes: Vec<BVHNode>, bvh_nodes: Vec<BVHNode>,
} }
#[derive(Debug, Default)]
struct Bin {
bounds: AABB,
tri_count: usize,
}
impl<M> fmt::Debug for BVHTriangles<M> impl<M> fmt::Debug for BVHTriangles<M>
where where
M: Material, M: Material,
@ -102,6 +108,7 @@ where
} }
} }
#[derive(Debug)]
struct SplitCost { struct SplitCost {
pos: f32, pos: f32,
axis: usize, axis: usize,
@ -175,6 +182,7 @@ where
info!("BVHTriangles build stats:"); info!("BVHTriangles build stats:");
info!(" Nodes: {}", stats.nodes); info!(" Nodes: {}", stats.nodes);
info!(" Leaves: {}", stats.leafs); info!(" Leaves: {}", stats.leafs);
info!(" Tris: {}", bvh.triangles.len());
info!(" Min Tri: {}", stats.min_tris); info!(" Min Tri: {}", stats.min_tris);
info!(" Max Tri: {}", stats.max_tris); info!(" Max Tri: {}", stats.max_tris);
info!(" Avg Tri: {}", bvh.triangles.len() / stats.leafs); info!(" Avg Tri: {}", bvh.triangles.len() / stats.leafs);
@ -226,20 +234,71 @@ where
}; };
for axis in 0..3 { for axis in 0..3 {
let bounds_min = node.aabb.min()[axis]; let mut bounds_min = f32::MAX;
let bounds_max = node.aabb.max()[axis]; let mut bounds_max = f32::MIN;
for i in 0..node.tri_count {
let triangle = &self.triangles[self.triangle_index[(node.left_first + i) as usize]];
bounds_min = bounds_min.min(triangle.centroid[axis]);
bounds_max = bounds_max.max(triangle.centroid[axis]);
}
if bounds_min == bounds_max { if bounds_min == bounds_max {
continue; continue;
} }
let scale = (bounds_max - bounds_min) / 100.;
for i in 0..100 { const NUM_BINS: usize = 8;
let candidate_pos = bounds_min + (i as f32) * scale; let mut bins: Vec<_> = (0..NUM_BINS)
let cost = self.evaluate_sah(node, axis, candidate_pos); .map(|_| Bin {
if cost <= best.cost { bounds: AABB::infinite(),
best.pos = candidate_pos; tri_count: 0,
})
.collect();
let scale = bins.len() as f32 / (bounds_max - bounds_min);
// populate the bins
for i in 0..node.tri_count {
let triangle = &self.triangles[self.triangle_index[(node.left_first + i) as usize]];
let bin_idx = (triangle.centroid[axis] - bounds_min) * scale;
let bin_idx = (bins.len() - 1).min(bin_idx as usize);
bins[bin_idx].tri_count += 1;
bins[bin_idx].bounds.grow(triangle.verts[0]);
bins[bin_idx].bounds.grow(triangle.verts[1]);
bins[bin_idx].bounds.grow(triangle.verts[2]);
}
// gather data for the 7 planes between the 8 bins
let mut left_area: Vec<_> = (0..bins.len() - 1).map(|_| 0.).collect();
let mut right_area: Vec<_> = (0..bins.len() - 1).map(|_| 0.).collect();
let mut left_count: Vec<_> = (0..bins.len() - 1).map(|_| 0).collect();
let mut right_count: Vec<_> = (0..bins.len() - 1).map(|_| 0).collect();
let mut left_box = AABB::infinite();
let mut right_box = AABB::infinite();
let mut left_sum = 0;
let mut right_sum = 0;
for i in 0..(bins.len() - 1) {
left_sum += bins[i].tri_count;
left_count[i] = left_sum;
left_box.grow(bins[i].bounds.min());
left_box.grow(bins[i].bounds.max());
left_area[i] = left_box.area();
right_sum += bins[bins.len() - 1 - i].tri_count;
right_count[bins.len() - 2 - i] = right_sum;
right_box.grow(bins[bins.len() - 1 - i].bounds.min());
right_box.grow(bins[bins.len() - 1 - i].bounds.max());
right_area[bins.len() - 2 - i] = right_box.area();
}
let scale = (bounds_max - bounds_min) / bins.len() as f32;
// calculate SAH cost for the 7 planes
for i in 0..(bins.len() - 1) {
let plane_cost =
left_count[i] as f32 * left_area[i] + right_count[i] as f32 * right_area[i];
if plane_cost < best.cost {
best.axis = axis; best.axis = axis;
best.cost = cost; best.pos = bounds_min + scale * (i as f32 + 1.);
best.cost = plane_cost;
} }
} }
} }
@ -306,34 +365,6 @@ where
self.subdivide(right_child_idx as usize); self.subdivide(right_child_idx as usize);
} }
fn evaluate_sah(&self, node: &BVHNode, axis: usize, pos: f32) -> f32 {
// determine triangle counts and bounds for this split candidate
let mut left_box = AABB::infinite();
let mut right_box = AABB::infinite();
let mut left_count = 0;
let mut right_count = 0;
for i in 0..node.tri_count {
let triangle = &self.triangles[self.triangle_index[(node.left_first + i) as usize]];
if triangle.centroid[axis] < pos {
left_count += 1;
left_box.grow(triangle.verts[0]);
left_box.grow(triangle.verts[1]);
left_box.grow(triangle.verts[2]);
} else {
right_count += 1;
right_box.grow(triangle.verts[0]);
right_box.grow(triangle.verts[1]);
right_box.grow(triangle.verts[2]);
}
}
let cost = left_count as f32 * left_box.area() + right_count as f32 * right_box.area();
if cost > 0. {
cost
} else {
f32::MAX
}
}
fn intersect_bvh(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { fn intersect_bvh(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
let mut node = &self.bvh_nodes[ROOT_NODE_IDX]; let mut node = &self.bvh_nodes[ROOT_NODE_IDX];
let mut stack = Vec::with_capacity(2); let mut stack = Vec::with_capacity(2);