Moved SAH splitting code into its own function.
This is in prep for the light tree, where we'll want to use that code.
This commit is contained in:
parent
2b05f65024
commit
1f94791b6b
76
src/bvh.rs
76
src/bvh.rs
|
@ -9,9 +9,9 @@ use boundable::Boundable;
|
||||||
use ray::AccelRay;
|
use ray::AccelRay;
|
||||||
use algorithm::{partition, merge_slices_append};
|
use algorithm::{partition, merge_slices_append};
|
||||||
use math::log2_64;
|
use math::log2_64;
|
||||||
|
use sah::sah_split;
|
||||||
|
|
||||||
const BVH_MAX_DEPTH: usize = 64;
|
const BVH_MAX_DEPTH: usize = 64;
|
||||||
const SAH_BIN_COUNT: usize = 13; // Prime numbers work best, for some reason
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BVH {
|
pub struct BVH {
|
||||||
|
@ -140,79 +140,7 @@ impl BVH {
|
||||||
let (split_index, split_axis) = if (log2_64(objects.len() as u64) as usize) <
|
let (split_index, split_axis) = if (log2_64(objects.len() as u64) as usize) <
|
||||||
(BVH_MAX_DEPTH - depth) {
|
(BVH_MAX_DEPTH - depth) {
|
||||||
// SAH splitting, when we have room to play
|
// SAH splitting, when we have room to play
|
||||||
|
sah_split(objects, &bounder)
|
||||||
// Pre-calc SAH div points
|
|
||||||
let sah_divs = {
|
|
||||||
let mut sah_divs = [[0.0f32; SAH_BIN_COUNT - 1]; 3];
|
|
||||||
for d in 0..3 {
|
|
||||||
let extent = bounds.max.get_n(d) - bounds.min.get_n(d);
|
|
||||||
for div in 0..(SAH_BIN_COUNT - 1) {
|
|
||||||
let part = extent * ((div + 1) as f32 / SAH_BIN_COUNT as f32);
|
|
||||||
sah_divs[d][div] = bounds.min.get_n(d) + part;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sah_divs
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build SAH bins
|
|
||||||
let sah_bins = {
|
|
||||||
let mut sah_bins = [[(BBox::new(), BBox::new(), 0, 0); SAH_BIN_COUNT - 1]; 3];
|
|
||||||
for obj in objects.iter() {
|
|
||||||
let tb = lerp_slice(bounder(obj), 0.5);
|
|
||||||
let centroid = (tb.min.into_vector() + tb.max.into_vector()) * 0.5;
|
|
||||||
|
|
||||||
for d in 0..3 {
|
|
||||||
for div in 0..(SAH_BIN_COUNT - 1) {
|
|
||||||
if centroid.get_n(d) <= sah_divs[d][div] {
|
|
||||||
sah_bins[d][div].0 |= tb;
|
|
||||||
sah_bins[d][div].2 += 1;
|
|
||||||
} else {
|
|
||||||
sah_bins[d][div].1 |= tb;
|
|
||||||
sah_bins[d][div].3 += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sah_bins
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find best split axis and div point
|
|
||||||
let (split_axis, div) = {
|
|
||||||
let mut dim = 0;
|
|
||||||
let mut div_n = 0.0;
|
|
||||||
let mut smallest_cost = std::f32::INFINITY;
|
|
||||||
|
|
||||||
for d in 0..3 {
|
|
||||||
for div in 0..(SAH_BIN_COUNT - 1) {
|
|
||||||
let left_cost = sah_bins[d][div].0.surface_area() *
|
|
||||||
sah_bins[d][div].2 as f32;
|
|
||||||
let right_cost = sah_bins[d][div].1.surface_area() *
|
|
||||||
sah_bins[d][div].3 as f32;
|
|
||||||
let tot_cost = left_cost + right_cost;
|
|
||||||
if tot_cost < smallest_cost {
|
|
||||||
dim = d;
|
|
||||||
div_n = sah_divs[d][div];
|
|
||||||
smallest_cost = tot_cost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(dim, div_n)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Partition
|
|
||||||
let mut split_i = partition(&mut objects[..], |obj| {
|
|
||||||
let tb = lerp_slice(bounder(obj), 0.5);
|
|
||||||
let centroid = (tb.min.get_n(split_axis) + tb.max.get_n(split_axis)) * 0.5;
|
|
||||||
centroid < div
|
|
||||||
});
|
|
||||||
if split_i < 1 {
|
|
||||||
split_i = 1;
|
|
||||||
} else if split_i >= objects.len() {
|
|
||||||
split_i = objects.len() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
(split_i, split_axis)
|
|
||||||
} else {
|
} else {
|
||||||
// Balanced splitting, when we don't have room to play
|
// Balanced splitting, when we don't have room to play
|
||||||
let split_axis = {
|
let split_axis = {
|
||||||
|
|
|
@ -29,6 +29,7 @@ mod triangle;
|
||||||
mod surface;
|
mod surface;
|
||||||
mod light;
|
mod light;
|
||||||
mod bvh;
|
mod bvh;
|
||||||
|
mod sah;
|
||||||
mod light_accel;
|
mod light_accel;
|
||||||
mod scene;
|
mod scene;
|
||||||
mod assembly;
|
mod assembly;
|
||||||
|
|
96
src/sah.rs
Normal file
96
src/sah.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use std;
|
||||||
|
|
||||||
|
use bbox::BBox;
|
||||||
|
use lerp::lerp_slice;
|
||||||
|
use algorithm::partition;
|
||||||
|
|
||||||
|
const SAH_BIN_COUNT: usize = 13; // Prime numbers work best, for some reason
|
||||||
|
|
||||||
|
/// Takes a slice of boundable objects and partitions them based on the Surface
|
||||||
|
/// Area Heuristic.
|
||||||
|
///
|
||||||
|
/// Returns the index of the partition boundary and the axis that it split on
|
||||||
|
/// (0 = x, 1 = y, 2 = z).
|
||||||
|
pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
|
||||||
|
where F: Fn(&T) -> &'a [BBox]
|
||||||
|
{
|
||||||
|
// Get combined object bounds
|
||||||
|
let bounds = {
|
||||||
|
let mut bb = BBox::new();
|
||||||
|
for obj in &objects[..] {
|
||||||
|
bb |= lerp_slice(bounder(obj), 0.5);
|
||||||
|
}
|
||||||
|
bb
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pre-calc SAH div points
|
||||||
|
let sah_divs = {
|
||||||
|
let mut sah_divs = [[0.0f32; SAH_BIN_COUNT - 1]; 3];
|
||||||
|
for d in 0..3 {
|
||||||
|
let extent = bounds.max.get_n(d) - bounds.min.get_n(d);
|
||||||
|
for div in 0..(SAH_BIN_COUNT - 1) {
|
||||||
|
let part = extent * ((div + 1) as f32 / SAH_BIN_COUNT as f32);
|
||||||
|
sah_divs[d][div] = bounds.min.get_n(d) + part;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sah_divs
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build SAH bins
|
||||||
|
let sah_bins = {
|
||||||
|
let mut sah_bins = [[(BBox::new(), BBox::new(), 0, 0); SAH_BIN_COUNT - 1]; 3];
|
||||||
|
for obj in objects.iter() {
|
||||||
|
let tb = lerp_slice(bounder(obj), 0.5);
|
||||||
|
let centroid = (tb.min.into_vector() + tb.max.into_vector()) * 0.5;
|
||||||
|
|
||||||
|
for d in 0..3 {
|
||||||
|
for div in 0..(SAH_BIN_COUNT - 1) {
|
||||||
|
if centroid.get_n(d) <= sah_divs[d][div] {
|
||||||
|
sah_bins[d][div].0 |= tb;
|
||||||
|
sah_bins[d][div].2 += 1;
|
||||||
|
} else {
|
||||||
|
sah_bins[d][div].1 |= tb;
|
||||||
|
sah_bins[d][div].3 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sah_bins
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find best split axis and div point
|
||||||
|
let (split_axis, div) = {
|
||||||
|
let mut dim = 0;
|
||||||
|
let mut div_n = 0.0;
|
||||||
|
let mut smallest_cost = std::f32::INFINITY;
|
||||||
|
|
||||||
|
for d in 0..3 {
|
||||||
|
for div in 0..(SAH_BIN_COUNT - 1) {
|
||||||
|
let left_cost = sah_bins[d][div].0.surface_area() * sah_bins[d][div].2 as f32;
|
||||||
|
let right_cost = sah_bins[d][div].1.surface_area() * sah_bins[d][div].3 as f32;
|
||||||
|
let tot_cost = left_cost + right_cost;
|
||||||
|
if tot_cost < smallest_cost {
|
||||||
|
dim = d;
|
||||||
|
div_n = sah_divs[d][div];
|
||||||
|
smallest_cost = tot_cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(dim, div_n)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Partition
|
||||||
|
let mut split_i = partition(&mut objects[..], |obj| {
|
||||||
|
let tb = lerp_slice(bounder(obj), 0.5);
|
||||||
|
let centroid = (tb.min.get_n(split_axis) + tb.max.get_n(split_axis)) * 0.5;
|
||||||
|
centroid < div
|
||||||
|
});
|
||||||
|
if split_i < 1 {
|
||||||
|
split_i = 1;
|
||||||
|
} else if split_i >= objects.len() {
|
||||||
|
split_i = objects.len() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(split_i, split_axis)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user