Generalized the BVH to... pretty much anything.
The BVH is now generic over any kind of data. The building function takes in a closure that can bound the given data type in 3d space, and the rest just works.
This commit is contained in:
parent
f87a8b4934
commit
1c660dda13
127
src/bvh.rs
127
src/bvh.rs
|
@ -7,8 +7,10 @@ use triangle;
|
||||||
use algorithm::partition;
|
use algorithm::partition;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BVH {
|
pub struct BVH<'a, T: 'a> {
|
||||||
nodes: Vec<BVHNode>,
|
nodes: Vec<BVHNode>,
|
||||||
|
objects: &'a [T],
|
||||||
|
depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -20,36 +22,51 @@ enum BVHNode {
|
||||||
|
|
||||||
Leaf {
|
Leaf {
|
||||||
bounds: BBox,
|
bounds: BBox,
|
||||||
triangle: (Point, Point, Point),
|
object_index: usize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BVH {
|
impl<'a, T> BVH<'a, T> {
|
||||||
pub fn from_triangles(triangles: &mut [(Point, Point, Point)]) -> BVH {
|
pub fn from_objects<F>(objects: &'a mut [T], bounder: F) -> BVH<'a, T>
|
||||||
let mut bvh = BVH { nodes: Vec::new() };
|
where F: Fn(&T) -> BBox
|
||||||
|
{
|
||||||
|
let mut bvh = BVH {
|
||||||
|
nodes: Vec::new(),
|
||||||
|
objects: &[],
|
||||||
|
depth: 0,
|
||||||
|
};
|
||||||
|
|
||||||
bvh.recursive_build(triangles);
|
bvh.recursive_build(0, 0, objects, &bounder);
|
||||||
|
bvh.objects = objects;
|
||||||
|
|
||||||
|
println!("BVH Depth: {}", bvh.depth);
|
||||||
|
|
||||||
bvh
|
bvh
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively builds the BVH starting at the given node with the given
|
|
||||||
// first and last primitive indices (in bag).
|
fn recursive_build<F>(&mut self,
|
||||||
fn recursive_build(&mut self, triangles: &mut [(Point, Point, Point)]) -> usize {
|
offset: usize,
|
||||||
|
depth: usize,
|
||||||
|
objects: &mut [T],
|
||||||
|
bounder: &F)
|
||||||
|
-> usize
|
||||||
|
where F: Fn(&T) -> BBox
|
||||||
|
{
|
||||||
let me = self.nodes.len();
|
let me = self.nodes.len();
|
||||||
|
|
||||||
if triangles.len() == 1 {
|
if objects.len() == 0 {
|
||||||
|
return 0;
|
||||||
|
} else if objects.len() == 1 {
|
||||||
// Leaf node
|
// Leaf node
|
||||||
let tri = triangles[0];
|
|
||||||
|
|
||||||
self.nodes.push(BVHNode::Leaf {
|
self.nodes.push(BVHNode::Leaf {
|
||||||
bounds: {
|
bounds: bounder(&objects[0]),
|
||||||
let minimum = tri.0.min(tri.1.min(tri.2));
|
object_index: offset,
|
||||||
let maximum = tri.0.max(tri.1.max(tri.2));
|
|
||||||
BBox::from_points(minimum, maximum)
|
|
||||||
},
|
|
||||||
triangle: tri,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if self.depth < depth {
|
||||||
|
self.depth = depth;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Not a leaf node
|
// Not a leaf node
|
||||||
self.nodes.push(BVHNode::Internal {
|
self.nodes.push(BVHNode::Internal {
|
||||||
|
@ -58,18 +75,10 @@ impl BVH {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Determine which axis to split on
|
// Determine which axis to split on
|
||||||
fn tri_bounds(tri: (Point, Point, Point)) -> BBox {
|
|
||||||
let minimum = tri.0.min(tri.1.min(tri.2));
|
|
||||||
let maximum = tri.0.max(tri.1.max(tri.2));
|
|
||||||
BBox {
|
|
||||||
min: minimum,
|
|
||||||
max: maximum,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let bounds = {
|
let bounds = {
|
||||||
let mut bounds = BBox::new();
|
let mut bounds = BBox::new();
|
||||||
for tri in &triangles[..] {
|
for obj in objects.iter() {
|
||||||
bounds = bounds | tri_bounds(*tri);
|
bounds = bounds | bounder(obj);
|
||||||
}
|
}
|
||||||
bounds
|
bounds
|
||||||
};
|
};
|
||||||
|
@ -87,10 +96,10 @@ impl BVH {
|
||||||
};
|
};
|
||||||
let split_pos = (bounds.min[split_axis] + bounds.max[split_axis]) * 0.5;
|
let split_pos = (bounds.min[split_axis] + bounds.max[split_axis]) * 0.5;
|
||||||
|
|
||||||
// Partition triangles based on split
|
// Partition objects based on split
|
||||||
let split_index = {
|
let split_index = {
|
||||||
let mut split_i = partition(triangles, |tri| {
|
let mut split_i = partition(objects, |obj| {
|
||||||
let tb = tri_bounds(*tri);
|
let tb = bounder(obj);
|
||||||
let centroid = (tb.min[split_axis] + tb.max[split_axis]) * 0.5;
|
let centroid = (tb.min[split_axis] + tb.max[split_axis]) * 0.5;
|
||||||
centroid < split_pos
|
centroid < split_pos
|
||||||
});
|
});
|
||||||
|
@ -102,8 +111,11 @@ impl BVH {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create child nodes
|
// Create child nodes
|
||||||
self.recursive_build(&mut triangles[..split_index]);
|
self.recursive_build(offset, depth + 1, &mut objects[..split_index], bounder);
|
||||||
let child2_index = self.recursive_build(&mut triangles[split_index..]);
|
let child2_index = self.recursive_build(offset + split_index,
|
||||||
|
depth + 1,
|
||||||
|
&mut objects[split_index..],
|
||||||
|
bounder);
|
||||||
|
|
||||||
// Set node
|
// Set node
|
||||||
self.nodes[me] = BVHNode::Internal {
|
self.nodes[me] = BVHNode::Internal {
|
||||||
|
@ -117,11 +129,18 @@ impl BVH {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn intersect_bvh(bvh: &BVH, ray: &Ray) -> bool {
|
pub fn intersect_bvh(bvh: &BVH<(Point, Point, Point)>, ray: &mut Ray) -> Option<(f32, f32, f32)> {
|
||||||
let mut i_stack = [0; 64];
|
if bvh.nodes.len() == 0 {
|
||||||
let mut stack_ptr = 0;
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
let mut i_stack = [0; 65];
|
||||||
|
let mut stack_ptr: usize = 1;
|
||||||
|
let mut hit = false;
|
||||||
|
let mut u = 0.0;
|
||||||
|
let mut v = 0.0;
|
||||||
|
|
||||||
|
while stack_ptr > 0 {
|
||||||
match bvh.nodes[i_stack[stack_ptr]] {
|
match bvh.nodes[i_stack[stack_ptr]] {
|
||||||
BVHNode::Internal { bounds, second_child_index } => {
|
BVHNode::Internal { bounds, second_child_index } => {
|
||||||
if bounds.intersect_ray(ray) {
|
if bounds.intersect_ray(ray) {
|
||||||
|
@ -129,25 +148,29 @@ pub fn intersect_bvh(bvh: &BVH, ray: &Ray) -> bool {
|
||||||
i_stack[stack_ptr + 1] = second_child_index;
|
i_stack[stack_ptr + 1] = second_child_index;
|
||||||
stack_ptr += 1;
|
stack_ptr += 1;
|
||||||
} else {
|
} else {
|
||||||
if stack_ptr == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stack_ptr -= 1;
|
stack_ptr -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BVHNode::Leaf{bounds: _, triangle: tri} => {
|
BVHNode::Leaf { bounds: _, object_index } => {
|
||||||
if let Some(_) = triangle::intersect_ray(ray, tri) {
|
if let Some((t, tri_u, tri_v)) =
|
||||||
return true;
|
triangle::intersect_ray(ray, bvh.objects[object_index]) {
|
||||||
|
if t < ray.max_t {
|
||||||
|
hit = true;
|
||||||
|
ray.max_t = t;
|
||||||
|
u = tri_u;
|
||||||
|
v = tri_v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_ptr -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hit {
|
||||||
|
return Some((ray.max_t, u, v));
|
||||||
} else {
|
} else {
|
||||||
if stack_ptr == 0 {
|
return None;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
stack_ptr -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
57
src/main.rs
57
src/main.rs
|
@ -20,6 +20,7 @@ use docopt::Docopt;
|
||||||
use image::Image;
|
use image::Image;
|
||||||
use math::{Point, Vector, fast_logit};
|
use math::{Point, Vector, fast_logit};
|
||||||
use ray::Ray;
|
use ray::Ray;
|
||||||
|
use bbox::BBox;
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -74,18 +75,32 @@ fn main() {
|
||||||
// Generate a scene of triangles
|
// Generate a scene of triangles
|
||||||
let mut triangles = {
|
let mut triangles = {
|
||||||
let mut triangles = Vec::new();
|
let mut triangles = Vec::new();
|
||||||
for x in 0..10 {
|
let xres = 512;
|
||||||
for y in 0..10 {
|
let yres = 512;
|
||||||
let cx = x as f32 * 32.0;
|
let xinc = 512.0 / (xres as f32);
|
||||||
let cy = y as f32 * 32.0;
|
let yinc = 512.0 / (yres as f32);
|
||||||
|
for x in 0..xres {
|
||||||
|
for y in 0..yres {
|
||||||
|
let cx = x as f32 * xinc;
|
||||||
|
let cy = y as f32 * yinc;
|
||||||
triangles.push((Point::new(cx, cy, 1.0),
|
triangles.push((Point::new(cx, cy, 1.0),
|
||||||
Point::new(cx + 32.0, cy, 1.0),
|
Point::new(cx + xinc, cy, 1.1),
|
||||||
Point::new(cx, cy + 32.0, 1.0)));
|
Point::new(cx, cy + yinc, 1.2)));
|
||||||
|
triangles.push((Point::new(cx + xinc, cy + yinc, 1.0),
|
||||||
|
Point::new(cx, cy + yinc, 1.1),
|
||||||
|
Point::new(cx + xinc, cy, 1.2)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
triangles
|
triangles
|
||||||
};
|
};
|
||||||
let scene = bvh::BVH::from_triangles(&mut triangles[..]);
|
let scene = bvh::BVH::from_objects(&mut triangles[..], |tri| {
|
||||||
|
let minimum = tri.0.min(tri.1.min(tri.2));
|
||||||
|
let maximum = tri.0.max(tri.1.max(tri.2));
|
||||||
|
BBox {
|
||||||
|
min: minimum,
|
||||||
|
max: maximum,
|
||||||
|
}
|
||||||
|
});
|
||||||
println!("Scene built.");
|
println!("Scene built.");
|
||||||
|
|
||||||
// Write output image of ray-traced triangle
|
// Write output image of ray-traced triangle
|
||||||
|
@ -96,23 +111,29 @@ fn main() {
|
||||||
let mut g = 0.0;
|
let mut g = 0.0;
|
||||||
let mut b = 0.0;
|
let mut b = 0.0;
|
||||||
let offset = hash_u32(((x as u32) << 16) ^ (y as u32), 0);
|
let offset = hash_u32(((x as u32) << 16) ^ (y as u32), 0);
|
||||||
const SAMPLES: usize = 64;
|
const SAMPLES: usize = 16;
|
||||||
for si in 0..SAMPLES {
|
for si in 0..SAMPLES {
|
||||||
let ray = Ray::new(Point::new(x as f32 +
|
let mut ray = Ray::new(Point::new(x as f32 +
|
||||||
fast_logit(halton::sample(0, offset + si as u32),
|
fast_logit(halton::sample(0,
|
||||||
|
offset + si as u32),
|
||||||
1.5),
|
1.5),
|
||||||
y as f32 +
|
y as f32 +
|
||||||
fast_logit(halton::sample(3, offset + si as u32),
|
fast_logit(halton::sample(3,
|
||||||
|
offset + si as u32),
|
||||||
1.5),
|
1.5),
|
||||||
0.0),
|
0.0),
|
||||||
Vector::new(0.0, 0.0, 1.0));
|
Vector::new(0.0, 0.0, 1.0));
|
||||||
if bvh::intersect_bvh(&scene, &ray) {
|
if let Some((_, u, v)) = bvh::intersect_bvh(&scene, &mut ray) {
|
||||||
r += 1.0;
|
r += u;
|
||||||
g += 1.0;
|
g += v;
|
||||||
b += 1.0;
|
b += (1.0 - u - v).max(0.0);
|
||||||
// r += u;
|
// r += 1.0;
|
||||||
// g += v;
|
// g += 1.0;
|
||||||
// b += (1.0 - u - v).max(0.0);
|
// b += 1.0;
|
||||||
|
} else {
|
||||||
|
r += 0.1;
|
||||||
|
g += 0.1;
|
||||||
|
b += 0.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r *= 255.0 / SAMPLES as f32;
|
r *= 255.0 / SAMPLES as f32;
|
||||||
|
|
|
@ -40,14 +40,20 @@ pub fn fast_ln(x: f32) -> f32 {
|
||||||
return y - 87.989971088;
|
return y - 87.989971088;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fast_logit(p: f32, width: f32) -> f32 {
|
|
||||||
let n = 0.001 + (p * 0.998);
|
|
||||||
|
|
||||||
fast_ln((n / (1.0 - n))) * width * (0.6266 / 4.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// The logit function, scaled to approximate the probit function.
|
||||||
|
///
|
||||||
|
/// We use this as a close approximation to the gaussian inverse CDF,
|
||||||
|
/// since the gaussian inverse CDF (probit) has no analytic formula.
|
||||||
pub fn logit(p: f32, width: f32) -> f32 {
|
pub fn logit(p: f32, width: f32) -> f32 {
|
||||||
let n = 0.001 + (p * 0.998);
|
let n = 0.001 + (p * 0.998);
|
||||||
|
|
||||||
(n / (1.0 - n)).ln() * width * (0.6266 / 4.0)
|
(n / (1.0 - n)).ln() * width * (0.6266 / 4.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fast_logit(p: f32, width: f32) -> f32 {
|
||||||
|
let n = 0.001 + (p * 0.998);
|
||||||
|
|
||||||
|
fast_ln((n / (1.0 - n))) * width * (0.6266 / 4.0)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user