rtiow: BVHTriangles use SAH for division and leave original triangles untouched.
This commit is contained in:
parent
df928e1779
commit
63975bad96
@ -3,6 +3,7 @@
|
|||||||
use std::f32::EPSILON;
|
use std::f32::EPSILON;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
use stl::STL;
|
use stl::STL;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -16,15 +17,15 @@ use crate::{
|
|||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct BVHNode {
|
struct BVHNode {
|
||||||
aabb: AABB,
|
aabb: AABB,
|
||||||
// When prim_count==0, left_first holds the left child's index in bvh_nodes. When >0 left_first
|
// When tri_count==0, left_first holds the left child's index in bvh_nodes. When >0 left_first
|
||||||
// holds the index for the first triangle in triangles.
|
// holds the index for the first triangle in triangles.
|
||||||
left_first: u32,
|
left_first: u32,
|
||||||
prim_count: u32,
|
tri_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BVHNode {
|
impl BVHNode {
|
||||||
fn is_leaf(&self) -> bool {
|
fn is_leaf(&self) -> bool {
|
||||||
self.prim_count > 0
|
self.tri_count > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ where
|
|||||||
M: Material,
|
M: Material,
|
||||||
{
|
{
|
||||||
pub triangles: Vec<Triangle>,
|
pub triangles: Vec<Triangle>,
|
||||||
|
triangle_index: Vec<usize>,
|
||||||
material: M,
|
material: M,
|
||||||
bvh_nodes: Vec<BVHNode>,
|
bvh_nodes: Vec<BVHNode>,
|
||||||
}
|
}
|
||||||
@ -80,7 +82,7 @@ where
|
|||||||
}
|
}
|
||||||
let n = &self.bvh_nodes[i];
|
let n = &self.bvh_nodes[i];
|
||||||
if n.is_leaf() {
|
if n.is_leaf() {
|
||||||
for t_idx in n.left_first..(n.left_first + n.prim_count) {
|
for t_idx in n.left_first..(n.left_first + n.tri_count) {
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
write!(f, "\t")?;
|
write!(f, "\t")?;
|
||||||
}
|
}
|
||||||
@ -101,6 +103,8 @@ where
|
|||||||
M: Material,
|
M: Material,
|
||||||
{
|
{
|
||||||
pub fn new(stl: &STL, material: M) -> BVHTriangles<M> {
|
pub fn new(stl: &STL, material: M) -> BVHTriangles<M> {
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
|
||||||
assert_eq!(std::mem::size_of::<BVHNode>(), 32);
|
assert_eq!(std::mem::size_of::<BVHNode>(), 32);
|
||||||
let div3 = 1. / 3.;
|
let div3 = 1. / 3.;
|
||||||
let triangles: Vec<_> = stl
|
let triangles: Vec<_> = stl
|
||||||
@ -117,15 +121,21 @@ where
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
let triangle_index = (0..triangles.len()).collect();
|
||||||
|
|
||||||
let n = 2 * triangles.len() - 2;
|
let n = 2 * triangles.len() - 2;
|
||||||
let bvh_nodes = Vec::with_capacity(n);
|
let bvh_nodes = Vec::with_capacity(n);
|
||||||
let mut bvh = BVHTriangles {
|
let mut bvh = BVHTriangles {
|
||||||
triangles,
|
triangles,
|
||||||
|
triangle_index,
|
||||||
bvh_nodes,
|
bvh_nodes,
|
||||||
material,
|
material,
|
||||||
};
|
};
|
||||||
bvh.build_bvh();
|
bvh.build_bvh();
|
||||||
|
info!(
|
||||||
|
"BVHTriangles build time {:0.3}s",
|
||||||
|
now.elapsed().as_secs_f32()
|
||||||
|
);
|
||||||
bvh
|
bvh
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +144,7 @@ where
|
|||||||
let root = BVHNode {
|
let root = BVHNode {
|
||||||
aabb: AABB::default(),
|
aabb: AABB::default(),
|
||||||
left_first: 0,
|
left_first: 0,
|
||||||
prim_count: self.triangles.len() as u32,
|
tri_count: self.triangles.len() as u32,
|
||||||
};
|
};
|
||||||
self.bvh_nodes.push(root);
|
self.bvh_nodes.push(root);
|
||||||
self.update_node_bounds(ROOT_NODE_IDX);
|
self.update_node_bounds(ROOT_NODE_IDX);
|
||||||
@ -145,8 +155,9 @@ where
|
|||||||
let node = &mut self.bvh_nodes[node_idx];
|
let node = &mut self.bvh_nodes[node_idx];
|
||||||
let mut aabb_min: Vec3 = f32::MAX.into();
|
let mut aabb_min: Vec3 = f32::MAX.into();
|
||||||
let mut aabb_max: Vec3 = f32::MIN.into();
|
let mut aabb_max: Vec3 = f32::MIN.into();
|
||||||
for i in node.left_first..(node.left_first + node.prim_count) {
|
for i in node.left_first..(node.left_first + node.tri_count) {
|
||||||
let leaf_tri = &self.triangles[i as usize];
|
let leaf_tri_idx = self.triangle_index[i as usize];
|
||||||
|
let leaf_tri = &self.triangles[leaf_tri_idx];
|
||||||
aabb_min = vec3::min(aabb_min, leaf_tri.verts[0]);
|
aabb_min = vec3::min(aabb_min, leaf_tri.verts[0]);
|
||||||
aabb_min = vec3::min(aabb_min, leaf_tri.verts[1]);
|
aabb_min = vec3::min(aabb_min, leaf_tri.verts[1]);
|
||||||
aabb_min = vec3::min(aabb_min, leaf_tri.verts[2]);
|
aabb_min = vec3::min(aabb_min, leaf_tri.verts[2]);
|
||||||
@ -157,36 +168,59 @@ where
|
|||||||
node.aabb = AABB::new(aabb_min, aabb_max);
|
node.aabb = AABB::new(aabb_min, aabb_max);
|
||||||
}
|
}
|
||||||
fn subdivide(&mut self, idx: usize) {
|
fn subdivide(&mut self, idx: usize) {
|
||||||
// Early out if we're down to just 2 or less triangles
|
let node = &self.bvh_nodes[idx];
|
||||||
if self.bvh_nodes[idx].prim_count <= 2 {
|
let parent_area = node.aabb.area();
|
||||||
return;
|
let parent_cost = node.tri_count as f32 * parent_area;
|
||||||
}
|
|
||||||
let (left_first, prim_count, left_count, i) = {
|
let (left_first, tri_count, left_count, i) = {
|
||||||
let node = &self.bvh_nodes[idx];
|
let node = &self.bvh_nodes[idx];
|
||||||
|
|
||||||
// Compute split plane and position.
|
let mut best_axis = usize::MAX;
|
||||||
let extent = node.aabb.max() - node.aabb.min();
|
let mut best_pos = 0.;
|
||||||
let axis = node.aabb.longest_axis();
|
let mut best_cost = f32::MAX;
|
||||||
let split_pos = node.aabb.min()[axis] + extent[axis] * 0.5;
|
|
||||||
|
for axis in 0..3 {
|
||||||
|
for i in 0..node.tri_count {
|
||||||
|
let triangle =
|
||||||
|
&self.triangles[self.triangle_index[(node.left_first + i) as usize]];
|
||||||
|
let candidate_pos = triangle.centroid[axis];
|
||||||
|
let cost = self.evaluate_sah(node, axis, candidate_pos);
|
||||||
|
if cost <= best_cost {
|
||||||
|
best_pos = candidate_pos;
|
||||||
|
best_axis = axis;
|
||||||
|
best_cost = cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Stop subdividing if it isn't getting any better.
|
||||||
|
if best_cost <= parent_cost {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let axis = best_axis;
|
||||||
|
let split_pos = best_pos;
|
||||||
|
|
||||||
// Split the group in two halves.
|
// Split the group in two halves.
|
||||||
let mut i = node.left_first as isize;
|
let mut i = node.left_first as isize;
|
||||||
let mut j = i + node.prim_count as isize - 1;
|
let mut j = i + node.tri_count as isize - 1;
|
||||||
while i <= j {
|
while i <= j {
|
||||||
if self.triangles[i as usize].centroid[axis] < split_pos {
|
if self.triangles[self.triangle_index[i as usize]].centroid[axis] < split_pos {
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
self.triangles.swap(i as usize, j as usize);
|
self.triangles.swap(
|
||||||
|
self.triangle_index[i as usize],
|
||||||
|
self.triangle_index[j as usize],
|
||||||
|
);
|
||||||
j -= 1;
|
j -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create child nodes for each half.
|
// Create child nodes for each half.
|
||||||
let left_count = i as u32 - node.left_first;
|
let left_count = i as u32 - node.left_first;
|
||||||
if left_count == 0 || left_count == node.prim_count {
|
if left_count == 0 || left_count == node.tri_count {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(node.left_first, node.prim_count, left_count, i)
|
(node.left_first, node.tri_count, left_count, i)
|
||||||
};
|
};
|
||||||
|
|
||||||
// create child nodes
|
// create child nodes
|
||||||
@ -195,27 +229,56 @@ where
|
|||||||
let left = BVHNode {
|
let left = BVHNode {
|
||||||
aabb: AABB::default(),
|
aabb: AABB::default(),
|
||||||
left_first,
|
left_first,
|
||||||
prim_count: left_count as u32,
|
tri_count: left_count as u32,
|
||||||
};
|
};
|
||||||
let right = BVHNode {
|
let right = BVHNode {
|
||||||
aabb: AABB::default(),
|
aabb: AABB::default(),
|
||||||
left_first: i as u32,
|
left_first: i as u32,
|
||||||
prim_count: (prim_count - left_count) as u32,
|
tri_count: (tri_count - left_count) as u32,
|
||||||
};
|
};
|
||||||
self.bvh_nodes.push(left);
|
self.bvh_nodes.push(left);
|
||||||
self.bvh_nodes.push(right);
|
self.bvh_nodes.push(right);
|
||||||
let node = &mut self.bvh_nodes[idx];
|
let node = &mut self.bvh_nodes[idx];
|
||||||
node.left_first = left_child_idx;
|
node.left_first = left_child_idx;
|
||||||
node.prim_count = 0;
|
node.tri_count = 0;
|
||||||
|
|
||||||
// Recurse
|
// Recurse
|
||||||
self.update_node_bounds(left_child_idx as usize);
|
self.update_node_bounds(left_child_idx as usize);
|
||||||
self.update_node_bounds(right_child_idx as usize);
|
|
||||||
self.subdivide(left_child_idx as usize);
|
self.subdivide(left_child_idx as usize);
|
||||||
|
self.update_node_bounds(right_child_idx as usize);
|
||||||
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, node_idx: u32, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
fn intersect_bvh(&self, r: Ray, node_idx: u32, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
||||||
|
// TODO(wathiede): visit nodes front to back and remove recursion.
|
||||||
let node = &self.bvh_nodes[node_idx as usize];
|
let node = &self.bvh_nodes[node_idx as usize];
|
||||||
if !node.aabb.hit(r, t_min, t_max) {
|
if !node.aabb.hit(r, t_min, t_max) {
|
||||||
return None;
|
return None;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user