From 1f94791b6b206f986c447466e9bb14d8e5731abc Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sat, 30 Jul 2016 21:20:01 -0700 Subject: [PATCH] Moved SAH splitting code into its own function. This is in prep for the light tree, where we'll want to use that code. --- src/bvh.rs | 76 ++---------------------------------------- src/main.rs | 1 + src/sah.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 74 deletions(-) create mode 100644 src/sah.rs diff --git a/src/bvh.rs b/src/bvh.rs index 08080e3..bb3538b 100644 --- a/src/bvh.rs +++ b/src/bvh.rs @@ -9,9 +9,9 @@ use boundable::Boundable; use ray::AccelRay; use algorithm::{partition, merge_slices_append}; use math::log2_64; +use sah::sah_split; const BVH_MAX_DEPTH: usize = 64; -const SAH_BIN_COUNT: usize = 13; // Prime numbers work best, for some reason #[derive(Debug)] pub struct BVH { @@ -140,79 +140,7 @@ impl BVH { let (split_index, split_axis) = if (log2_64(objects.len() as u64) as usize) < (BVH_MAX_DEPTH - depth) { // SAH splitting, when we have room to play - - // 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) + sah_split(objects, &bounder) } else { // Balanced splitting, when we don't have room to play let split_axis = { diff --git a/src/main.rs b/src/main.rs index 4d77f29..cd211f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,7 @@ mod triangle; mod surface; mod light; mod bvh; +mod sah; mod light_accel; mod scene; mod assembly; diff --git a/src/sah.rs b/src/sah.rs new file mode 100644 index 0000000..61ede52 --- /dev/null +++ b/src/sah.rs @@ -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) +}