Implemented alternative SAH split that uses off-axis split planes.
It doesn't seem to actually perform better, generally, but I'm leaving it in for future reference for other things.
This commit is contained in:
parent
c71b00ca31
commit
7f1ab59c5e
16
src/bbox.rs
16
src/bbox.rs
|
@ -113,6 +113,22 @@ impl BitOrAssign for BBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expand BBox by a point.
|
||||||
|
impl BitOr<Point> for BBox {
|
||||||
|
type Output = BBox;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: Point) -> BBox {
|
||||||
|
BBox::from_points(Point { co: self.min.co.v_min(rhs.co) },
|
||||||
|
Point { co: self.max.co.v_max(rhs.co) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOrAssign<Point> for BBox {
|
||||||
|
fn bitor_assign(&mut self, rhs: Point) {
|
||||||
|
*self = *self | rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Lerp for BBox {
|
impl Lerp for BBox {
|
||||||
fn lerp(self, other: BBox, alpha: f32) -> BBox {
|
fn lerp(self, other: BBox, alpha: f32) -> BBox {
|
||||||
|
|
|
@ -1,12 +1,151 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std;
|
use std;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use algorithm::{partition, quick_select};
|
use algorithm::{partition, quick_select};
|
||||||
use bbox::BBox;
|
use bbox::BBox;
|
||||||
|
use halton;
|
||||||
use lerp::lerp_slice;
|
use lerp::lerp_slice;
|
||||||
|
use math::{Vector, dot};
|
||||||
|
use sampling::uniform_sample_hemisphere;
|
||||||
|
|
||||||
|
|
||||||
const SAH_BIN_COUNT: usize = 13; // Prime numbers work best, for some reason
|
const SAH_BIN_COUNT: usize = 13; // Prime numbers work best, for some reason
|
||||||
|
const SPLIT_PLANE_COUNT: usize = 5;
|
||||||
|
|
||||||
|
|
||||||
|
/// Takes a slice of boundable objects and partitions them based on the Surface
|
||||||
|
/// Area Heuristic, but using arbitrarily oriented planes.
|
||||||
|
///
|
||||||
|
/// Returns the index of the partition boundary and the axis that it split on
|
||||||
|
/// (0 = x, 1 = y, 2 = z).
|
||||||
|
pub fn free_sah_split<'a, T, F>(seed: u32, objects: &mut [T], bounder: &F) -> (usize, usize)
|
||||||
|
where F: Fn(&T) -> &'a [BBox]
|
||||||
|
{
|
||||||
|
// Generate the planes for splitting
|
||||||
|
let planes = {
|
||||||
|
let mut planes = [Vector::new(0.0, 0.0, 0.0); SPLIT_PLANE_COUNT];
|
||||||
|
let offset = seed * SPLIT_PLANE_COUNT as u32;
|
||||||
|
for i in 0..SPLIT_PLANE_COUNT {
|
||||||
|
let u = halton::sample(0, offset + i as u32);
|
||||||
|
let v = halton::sample(1, offset + i as u32);
|
||||||
|
planes[i] = uniform_sample_hemisphere(u, v).normalized();
|
||||||
|
}
|
||||||
|
planes
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the extents of the objects with respect to the split planes
|
||||||
|
let extents = {
|
||||||
|
let mut extents = [(std::f32::INFINITY, std::f32::NEG_INFINITY); SPLIT_PLANE_COUNT];
|
||||||
|
for obj in &objects[..] {
|
||||||
|
let centroid = lerp_slice(bounder(obj), 0.5).center().into_vector();
|
||||||
|
for i in 0..SPLIT_PLANE_COUNT {
|
||||||
|
let dist = dot(centroid, planes[i]);
|
||||||
|
extents[i].0 = extents[i].0.min(dist);
|
||||||
|
extents[i].1 = extents[i].1.max(dist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extents
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pre-calc SAH div distances
|
||||||
|
let sah_divs = {
|
||||||
|
let mut sah_divs = [[0.0f32; SAH_BIN_COUNT - 1]; SPLIT_PLANE_COUNT];
|
||||||
|
for pi in 0..SPLIT_PLANE_COUNT {
|
||||||
|
let extent = extents[pi].1 - extents[pi].0;
|
||||||
|
for div in 0..(SAH_BIN_COUNT - 1) {
|
||||||
|
let part = extent * ((div + 1) as f32 / SAH_BIN_COUNT as f32);
|
||||||
|
sah_divs[pi][div] = extents[pi].0 + part;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sah_divs
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build SAH bins
|
||||||
|
let sah_bins = {
|
||||||
|
let mut sah_bins =
|
||||||
|
[[(BBox::new(), BBox::new(), 0, 0); SAH_BIN_COUNT - 1]; SPLIT_PLANE_COUNT];
|
||||||
|
for obj in objects.iter() {
|
||||||
|
let tb = lerp_slice(bounder(obj), 0.5);
|
||||||
|
let centroid = tb.center().into_vector();
|
||||||
|
|
||||||
|
for pi in 0..SPLIT_PLANE_COUNT {
|
||||||
|
for div in 0..(SAH_BIN_COUNT - 1) {
|
||||||
|
let dist = dot(centroid, planes[pi]);
|
||||||
|
if dist <= sah_divs[pi][div] {
|
||||||
|
sah_bins[pi][div].0 |= tb;
|
||||||
|
sah_bins[pi][div].2 += 1;
|
||||||
|
} else {
|
||||||
|
sah_bins[pi][div].1 |= tb;
|
||||||
|
sah_bins[pi][div].3 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sah_bins
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find best split axis and div point
|
||||||
|
let (split_plane_i, div_n) = {
|
||||||
|
let mut split_plane_i = 0;
|
||||||
|
let mut div_n = 0.0;
|
||||||
|
let mut smallest_cost = std::f32::INFINITY;
|
||||||
|
|
||||||
|
for pi in 0..SPLIT_PLANE_COUNT {
|
||||||
|
for div in 0..(SAH_BIN_COUNT - 1) {
|
||||||
|
let left_cost = sah_bins[pi][div].0.surface_area() * sah_bins[pi][div].2 as f32;
|
||||||
|
let right_cost = sah_bins[pi][div].1.surface_area() * sah_bins[pi][div].3 as f32;
|
||||||
|
let tot_cost = left_cost + right_cost;
|
||||||
|
if tot_cost < smallest_cost {
|
||||||
|
split_plane_i = pi;
|
||||||
|
div_n = sah_divs[pi][div];
|
||||||
|
smallest_cost = tot_cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(split_plane_i, div_n)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate the approximate axis-aligned split, along with flipping the split plane as
|
||||||
|
// appropriate.
|
||||||
|
let (plane, approx_axis, div) = {
|
||||||
|
// Find axis with largest value
|
||||||
|
let mut largest_axis = 0;
|
||||||
|
let mut n = 0.0;
|
||||||
|
for d in 0..3 {
|
||||||
|
let m = planes[split_plane_i].get_n(d).abs();
|
||||||
|
if n < m {
|
||||||
|
largest_axis = d;
|
||||||
|
n = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's negative, flip
|
||||||
|
if planes[split_plane_i].get_n(largest_axis).is_sign_positive() {
|
||||||
|
(planes[split_plane_i], largest_axis, div_n)
|
||||||
|
} else {
|
||||||
|
(planes[split_plane_i] * -1.0, largest_axis, div_n * -1.0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Partition
|
||||||
|
let mut split_i = partition(&mut objects[..], |obj| {
|
||||||
|
let centroid = lerp_slice(bounder(obj), 0.5).center().into_vector();
|
||||||
|
let dist = dot(centroid, plane);
|
||||||
|
dist < div
|
||||||
|
});
|
||||||
|
|
||||||
|
if split_i < 1 {
|
||||||
|
split_i = 1;
|
||||||
|
} else if split_i >= objects.len() {
|
||||||
|
split_i = objects.len() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(split_i, approx_axis)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Takes a slice of boundable objects and partitions them based on the Surface
|
/// Takes a slice of boundable objects and partitions them based on the Surface
|
||||||
/// Area Heuristic.
|
/// Area Heuristic.
|
||||||
|
@ -16,11 +155,11 @@ const SAH_BIN_COUNT: usize = 13; // Prime numbers work best, for some reason
|
||||||
pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
|
pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
|
||||||
where F: Fn(&T) -> &'a [BBox]
|
where F: Fn(&T) -> &'a [BBox]
|
||||||
{
|
{
|
||||||
// Get combined object bounds
|
// Get combined object centroid extents
|
||||||
let bounds = {
|
let bounds = {
|
||||||
let mut bb = BBox::new();
|
let mut bb = BBox::new();
|
||||||
for obj in &objects[..] {
|
for obj in &objects[..] {
|
||||||
bb |= lerp_slice(bounder(obj), 0.5);
|
bb |= lerp_slice(bounder(obj), 0.5).center();
|
||||||
}
|
}
|
||||||
bb
|
bb
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user