From c92a8c4da0dc642a41c1a0b9580fdc7983e9f207 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sun, 23 Apr 2017 23:15:31 -0700 Subject: [PATCH] During BVH construction, merge BBox time samples based on a threshold. If the average surface area of all the time samples is close enough to the surface area of their union, just take the union and use that. This both makes the BVH smaller in memory (time samples don't propigate up the tree beyond their usefulness) and makes it faster since traversal can avoid interpolating BBoxes when there's only one BBox for a node. --- src/accel/bvh.rs | 6 +++-- src/accel/bvh_base.rs | 48 +++++++++++++++++++++++++++++------- src/bbox.rs | 7 ++---- src/scene/assembly.rs | 14 +++++------ src/surface/triangle_mesh.rs | 12 ++++----- 5 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/accel/bvh.rs b/src/accel/bvh.rs index 8a57592..c8c3ab5 100644 --- a/src/accel/bvh.rs +++ b/src/accel/bvh.rs @@ -128,9 +128,11 @@ impl<'a> BVH<'a> { -> &'a mut BVHNode<'a> { match &base.nodes[node_index] { &BVHBaseNode::Internal { bounds_range, children_indices, split_axis } => { - let mut node = unsafe { arena.alloc_uninitialized::() }; + let mut node = unsafe { arena.alloc_uninitialized_with_alignment::(32) }; - let bounds = arena.copy_slice(&base.bounds[bounds_range.0..bounds_range.1]); + let bounds = + arena.copy_slice_with_alignment(&base.bounds[bounds_range.0..bounds_range.1], + 32); let child1 = BVH::construct_from_base(arena, base, children_indices.0); let child2 = BVH::construct_from_base(arena, base, children_indices.1); diff --git a/src/accel/bvh_base.rs b/src/accel/bvh_base.rs index 56ed1dc..16cc720 100644 --- a/src/accel/bvh_base.rs +++ b/src/accel/bvh_base.rs @@ -10,6 +10,11 @@ use super::objects_split::{sah_split, median_split}; pub const BVH_MAX_DEPTH: usize = 42; +// Amount bigger the union of all time samples can be +// and still use the union rather than preserve the +// individual time samples. +const USE_UNION_FACTOR: f32 = 1.4; + /// An intermediary structure for creating a BVH. #[derive(Debug)] pub struct BVHBase { @@ -104,11 +109,24 @@ impl BVHBase { return (0, (0, 0)); } else if objects.len() <= objects_per_leaf { // Leaf node - self.acc_bounds(objects, bounder); let bi = self.bounds.len(); - for b in self.bounds_cache.iter() { - self.bounds.push(*b); + // Get bounds + { + // We make sure that it's worth having multiple time samples, and if not + // we reduce to the union of the time samples. + self.acc_bounds(objects, bounder); + let union_bounds = self.bounds_cache.iter().fold(BBox::new(), |b1, b2| (b1 | *b2)); + let average_area = + self.bounds_cache.iter().fold(0.0, |area, bb| area + bb.surface_area()) / + self.bounds_cache.len() as f32; + if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) { + self.bounds.push(union_bounds); + } else { + self.bounds.extend(&self.bounds_cache); + } } + + // Create node self.nodes.push(BVHBaseNode::Leaf { bounds_range: (bi, self.bounds.len()), object_range: (offset, offset + objects.len()), @@ -155,12 +173,24 @@ impl BVHBase { // Determine bounds // TODO: do merging without the temporary vec. let bi = self.bounds.len(); - let mut merged = Vec::new(); - merge_slices_append(&self.bounds[c1_bounds.0..c1_bounds.1], - &self.bounds[c2_bounds.0..c2_bounds.1], - &mut merged, - |b1, b2| *b1 | *b2); - self.bounds.extend(merged.drain(0..)); + { + let mut merged = Vec::new(); + merge_slices_append(&self.bounds[c1_bounds.0..c1_bounds.1], + &self.bounds[c2_bounds.0..c2_bounds.1], + &mut merged, + |b1, b2| *b1 | *b2); + // We make sure that it's worth having multiple time samples, and if not + // we reduce to the union of the time samples. + let union_bounds = merged.iter().fold(BBox::new(), |b1, b2| (b1 | *b2)); + let average_area = merged.iter().fold(0.0, |area, bb| area + bb.surface_area()) / + merged.len() as f32; + if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) { + self.bounds.push(union_bounds); + } else { + self.bounds.extend(merged.drain(0..)); + } + } + // Set node self.nodes[me] = BVHBaseNode::Internal { diff --git a/src/bbox.rs b/src/bbox.rs index ae553a6..9bd8613 100644 --- a/src/bbox.rs +++ b/src/bbox.rs @@ -80,11 +80,8 @@ impl BBox { } pub fn surface_area(&self) -> f32 { - let x = self.max.x() - self.min.x(); - let y = self.max.y() - self.min.y(); - let z = self.max.z() - self.min.z(); - - ((x * y) + (y * z) + (z * x)) * 2.0 + let d = self.max - self.min; + ((d.x() * d.y()) + (d.y() * d.z()) + (d.z() * d.x())) * 2.0 } pub fn center(&self) -> Point { diff --git a/src/scene/assembly.rs b/src/scene/assembly.rs index 5c0a271..1883640 100644 --- a/src/scene/assembly.rs +++ b/src/scene/assembly.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use mem_arena::MemArena; use accel::{LightAccel, LightTree}; -use accel::BVH4; +use accel::BVH; use bbox::{BBox, transform_bbox_slice_from}; use boundable::Boundable; use color::SpectralSample; @@ -28,7 +28,7 @@ pub struct Assembly<'a> { pub assemblies: &'a [Assembly<'a>], // Object accel - pub object_accel: BVH4<'a>, + pub object_accel: BVH<'a>, // Light accel pub light_accel: LightTree<'a>, @@ -231,10 +231,10 @@ impl<'a> AssemblyBuilder<'a> { let (bis, bbs) = self.instance_bounds(); // Build object accel - let object_accel = BVH4::from_objects(self.arena, - &mut self.instances[..], - 1, - |inst| &bbs[bis[inst.id]..bis[inst.id + 1]]); + let object_accel = BVH::from_objects(self.arena, + &mut self.instances[..], + 1, + |inst| &bbs[bis[inst.id]..bis[inst.id + 1]]); // Get list of instances that are for light sources or assemblies that contain light // sources. @@ -288,7 +288,7 @@ impl<'a> AssemblyBuilder<'a> { /// Returns a pair of vectors with the bounds of all instances. - /// This is used for building the assembly's BVH4. + /// This is used for building the assembly's BVH. fn instance_bounds(&self) -> (Vec, Vec) { let mut indices = vec![0]; let mut bounds = Vec::new(); diff --git a/src/surface/triangle_mesh.rs b/src/surface/triangle_mesh.rs index c3971dc..728486f 100644 --- a/src/surface/triangle_mesh.rs +++ b/src/surface/triangle_mesh.rs @@ -2,7 +2,7 @@ use mem_arena::MemArena; -use accel::BVH4; +use accel::BVH; use bbox::BBox; use boundable::Boundable; use color::XYZ; @@ -20,7 +20,7 @@ pub struct TriangleMesh<'a> { time_samples: usize, geo: &'a [(Point, Point, Point)], indices: &'a [usize], - accel: BVH4<'a>, + accel: BVH<'a>, } impl<'a> TriangleMesh<'a> { @@ -44,10 +44,10 @@ impl<'a> TriangleMesh<'a> { bounds }; - let accel = BVH4::from_objects(arena, - &mut indices[..], - 3, - |tri_i| &bounds[*tri_i..(*tri_i + time_samples)]); + let accel = BVH::from_objects(arena, + &mut indices[..], + 3, + |tri_i| &bounds[*tri_i..(*tri_i + time_samples)]); TriangleMesh { time_samples: time_samples,