Add some faster AABB hit implementations.

Switch e?println to info/trace logging.
This commit is contained in:
2018-09-18 17:48:27 -07:00
parent 4a9754dfdb
commit aa26c79f6d
9 changed files with 253 additions and 31 deletions

View File

@@ -5,29 +5,104 @@ use vec3::Vec3;
#[derive(Debug, Clone, PartialEq)]
pub struct AABB {
pub min: Vec3,
pub max: Vec3,
bounds: [Vec3; 2],
}
impl fmt::Display for AABB {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({})-({})", self.min, self.max)
write!(f, "({})-({})", self.bounds[0], self.bounds[1])
}
}
fn min(x: f32, y: f32) -> f32 {
if x < y {
x
} else {
y
}
}
fn max(x: f32, y: f32) -> f32 {
if x > y {
x
} else {
y
}
}
impl AABB {
pub fn new(min: Vec3, max: Vec3) -> AABB {
AABB { min, max }
AABB { bounds: [min, max] }
}
pub fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
pub fn min(&self) -> Vec3 {
self.bounds[0]
}
pub fn max(&self) -> Vec3 {
self.bounds[1]
}
pub fn hit2(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
let mut t_min = t_min;
let mut t_max = t_max;
for axis in 0..3 {
let t0 = ((self.min[axis] - r.origin[axis]) / r.direction[axis])
.min((self.max[axis] - r.origin[axis]) / r.direction[axis]);
let t1 = ((self.min[axis] - r.origin[axis]) / r.direction[axis])
.max((self.max[axis] - r.origin[axis]) / r.direction[axis]);
let d_inv = 1.0 / r.direction[axis];
let t0 = min(
(self.min()[axis] - r.origin[axis]) * d_inv,
(self.max()[axis] - r.origin[axis]) * d_inv,
);
let t1 = max(
(self.min()[axis] - r.origin[axis]) * d_inv,
(self.max()[axis] - r.origin[axis]) * d_inv,
);
t_min = max(t0, t_min);
t_max = min(t1, t_max);
if t_max <= t_min {
return false;
}
}
true
}
pub fn hit(&self, r: Ray, t0: f32, t1: f32) -> bool {
let mut t_min = (self.bounds[r.sign[0]].x - r.origin.x) * r.inv_direction.x;
let mut t_max = (self.bounds[1 - r.sign[0]].x - r.origin.x) * r.inv_direction.x;
let t_y_min = (self.bounds[r.sign[0]].y - r.origin.y) * r.inv_direction.y;
let t_y_max = (self.bounds[1 - r.sign[0]].y - r.origin.y) * r.inv_direction.y;
if t_min > t_y_max || t_y_min > t_max {
return false;
}
if t_y_min > t_min {
t_min = t_y_min;
}
if t_y_max < t_max {
t_max = t_y_max;
}
let t_z_min = (self.bounds[r.sign[2]].z - r.origin.z) * r.inv_direction.z;
let t_z_max = (self.bounds[1 - r.sign[2]].z - r.origin.z) * r.inv_direction.z;
if t_min > t_z_max || t_z_min > t_max {
return false;
}
if t_z_min > t_min {
t_min = t_z_min;
}
if t_z_max < t_max {
t_max = t_z_max;
}
t_min < t1 && t_max > t0
}
pub fn hit_original(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
let mut t_min = t_min;
let mut t_max = t_max;
for axis in 0..3 {
let t0 = ((self.min()[axis] - r.origin[axis]) * r.inv_direction[axis])
.min((self.max()[axis] - r.origin[axis]) * r.inv_direction[axis]);
let t1 = ((self.min()[axis] - r.origin[axis]) * r.inv_direction[axis])
.max((self.max()[axis] - r.origin[axis]) * r.inv_direction[axis]);
t_min = t0.max(t_min);
t_max = t1.min(t_max);
if t_max <= t_min {
@@ -36,18 +111,37 @@ impl AABB {
}
true
}
pub fn hit_fast(&self, r: Ray, _t_min: f32, _t_max: f32) -> bool {
let b = self;
let mut t1 = (b.min()[0] - r.origin[0]) * 1. / r.direction[0];
let mut t2 = (b.max()[0] - r.origin[0]) * 1. / r.direction[0];
let mut tmin = t1.min(t2);
let mut tmax = t1.max(t2);
for i in 1..3 {
t1 = (b.min()[i] - r.origin[i]) * 1. / r.direction[i];
t2 = (b.max()[i] - r.origin[i]) * 1. / r.direction[i];
tmin = tmin.max(t1.min(t2));
tmax = tmax.min(t1.max(t2));
}
return tmax > tmin.max(0.0);
}
}
pub fn surrounding_box(box0: &AABB, box1: &AABB) -> AABB {
let min = Vec3::new(
box0.min.x.min(box1.min.x),
box0.min.y.min(box1.min.y),
box0.min.z.min(box1.min.z),
box0.min().x.min(box1.min().x),
box0.min().y.min(box1.min().y),
box0.min().z.min(box1.min().z),
);
let max = Vec3::new(
box0.max.x.max(box1.max.x),
box0.max.y.max(box1.max.y),
box0.max.z.max(box1.max.z),
box0.max().x.max(box1.max().x),
box0.max().y.max(box1.max().y),
box0.max().z.max(box1.max().z),
);
AABB::new(min, max)
}

View File

@@ -1,5 +1,8 @@
#[macro_use]
extern crate log;
extern crate rand;
extern crate rtiow;
extern crate stderrlog;
#[macro_use]
extern crate structopt;
@@ -114,7 +117,9 @@ fn build_scene_book(bvh: bool, opt: &Opt) -> Scene {
);
let world: Box<Hit>;
if bvh {
world = Box::new(BVH::new(random_scene(), time_min, time_max));
let b = BVH::new(random_scene(), time_min, time_max);
trace!(target: "bvh", "World {}", b);
world = Box::new(b);
} else {
world = Box::new(HitableList::new(random_scene()));
}
@@ -145,7 +150,7 @@ fn build_scene_bvh(opt: &Opt) -> Scene {
time_min,
time_max,
);
let world: Box<Hit> = Box::new(BVH::new(
let b = BVH::new(
vec![
Box::new(Sphere::new(
Vec3::new(0., 0., -1.),
@@ -173,7 +178,9 @@ fn build_scene_bvh(opt: &Opt) -> Scene {
],
time_min,
time_max,
));
);
trace!(target: "bvh", "World {}", b);
let world: Box<Hit> = Box::new(b);
Scene {
camera,
world,
@@ -332,6 +339,11 @@ pub struct Opt {
}
fn main() -> Result<(), std::io::Error> {
stderrlog::new()
.verbosity(3)
.timestamp(stderrlog::Timestamp::Millisecond)
.init()
.unwrap();
let start = Instant::now();
let opt = Opt::from_args();
let scene = match opt.model {
@@ -343,7 +355,7 @@ fn main() -> Result<(), std::io::Error> {
};
let res = render(scene, &opt.output);
let runtime = start.elapsed();
eprintln!(
info!(
"Render time {}.{} seconds",
runtime.as_secs(),
runtime.subsec_millis()

View File

@@ -1,5 +1,6 @@
use std;
use std::fmt;
use std::time::Instant;
use rand;
use rand::Rng;
@@ -43,7 +44,7 @@ impl fmt::Display for BVHNode {
fn box_x_compare(ah: &Box<Hit>, bh: &Box<Hit>) -> std::cmp::Ordering {
match (ah.bounding_box(0., 0.), bh.bounding_box(0., 0.)) {
(Some(box_left), Some(box_right)) => {
return box_left.min.x.partial_cmp(&box_right.min.x).unwrap();
return box_left.min().x.partial_cmp(&box_right.min().x).unwrap();
}
_ => panic!("hit missing bounding box"),
}
@@ -52,7 +53,7 @@ fn box_x_compare(ah: &Box<Hit>, bh: &Box<Hit>) -> std::cmp::Ordering {
fn box_y_compare(ah: &Box<Hit>, bh: &Box<Hit>) -> std::cmp::Ordering {
match (ah.bounding_box(0., 0.), bh.bounding_box(0., 0.)) {
(Some(box_left), Some(box_right)) => {
return box_left.min.y.partial_cmp(&box_right.min.y).unwrap();
return box_left.min().y.partial_cmp(&box_right.min().y).unwrap();
}
_ => panic!("hit missing bounding box"),
}
@@ -61,7 +62,7 @@ fn box_y_compare(ah: &Box<Hit>, bh: &Box<Hit>) -> std::cmp::Ordering {
fn box_z_compare(ah: &Box<Hit>, bh: &Box<Hit>) -> std::cmp::Ordering {
match (ah.bounding_box(0., 0.), bh.bounding_box(0., 0.)) {
(Some(box_left), Some(box_right)) => {
return box_left.min.z.partial_cmp(&box_right.min.z).unwrap();
return box_left.min().z.partial_cmp(&box_right.min().z).unwrap();
}
_ => panic!("hit missing bounding box"),
}
@@ -115,6 +116,13 @@ impl BVHNode {
(None, None) => None,
}
}
fn volume(&self) -> f32 {
let bbox = self.bounding_box(0., 0.).unwrap();
(bbox.min().x - bbox.max().x).abs()
* (bbox.min().y - bbox.max().y).abs()
* (bbox.min().z - bbox.max().z).abs()
}
}
impl Hit for BVHNode {
@@ -152,15 +160,25 @@ pub struct BVH {
impl BVH {
pub fn new(l: Vec<Box<Hit>>, t_min: f32, t_max: f32) -> BVH {
BVH {
let count = l.len();
let start = Instant::now();
let bvh = BVH {
root: BVHNode::new(l, t_min, t_max),
}
};
let runtime = start.elapsed();
info!(
"BVH build time {}.{} seconds for {} hitables",
runtime.as_secs(),
runtime.subsec_millis(),
count
);
bvh
}
}
fn print_tree(f: &mut fmt::Formatter, depth: usize, bvhn: &BVHNode) -> fmt::Result {
// TODO(wathiede): recurse and indent
write!(f, "{}{}\n", " ".repeat(depth * 2), bvhn)?;
let vol = bvhn.volume();
write!(f, "{:.*}{}{}\n", 2, vol, " ".repeat(depth * 2), bvhn)?;
if let BVHNode::Branch { left, right, .. } = bvhn {
print_tree(f, depth + 1, left)?;
print_tree(f, depth + 1, right)?;

View File

@@ -13,6 +13,8 @@ pub mod vec3;
extern crate crossbeam_channel;
extern crate image;
#[macro_use]
extern crate log;
extern crate num_cpus;
extern crate rand;
extern crate rayon;

View File

@@ -5,14 +5,23 @@ pub struct Ray {
pub origin: Vec3,
pub direction: Vec3,
pub time: f32,
pub inv_direction: Vec3,
pub sign: [usize; 3],
}
impl Ray {
pub fn new(origin: Vec3, direction: Vec3, time: f32) -> Ray {
let inv = 1. / direction;
Ray {
origin,
direction,
time,
inv_direction: inv,
sign: [
(inv.x < 0.) as usize,
(inv.y < 0.) as usize,
(inv.z < 0.) as usize,
],
}
}
pub fn point_at_parameter(self, t: f32) -> Vec3 {

View File

@@ -47,6 +47,7 @@ fn trace_pixel(x: usize, y: usize, scene: &Scene) -> Vec3 {
}
fn render_worker(
tid: usize,
scene: &Scene,
input_chan: channel::Receiver<usize>,
output_chan: channel::Sender<(usize, Vec<Vec3>)>,
@@ -61,7 +62,7 @@ fn render_worker(
}
output_chan.send((subsample, pixel_data));
}
eprintln!("Shutting down worker");
info!("Shutting down worker {}", tid);
}
pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::io::Error> {
@@ -69,12 +70,12 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
let (pixel_data_tx, pixel_data_rx) = channel::unbounded();
let scene = sync::Arc::new(scene);
for _ in 0..num_cpus::get() {
for i in 0..num_cpus::get() {
let s = sync::Arc::clone(&scene);
let seq_rx = seq_rx.clone();
let pixel_data_tx = pixel_data_tx.clone();
thread::spawn(move || {
render_worker(&s, seq_rx, pixel_data_tx);
render_worker(i, &s, seq_rx, pixel_data_tx);
});
}
drop(seq_rx);
@@ -113,12 +114,12 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
);
});
let path = output_dir.join(format!("iteration{:05}.png", acc_count));
eprintln!("saving {}", path.to_string_lossy());
trace!(target: "renderer", "Saving {}", path.to_string_lossy());
img.save(&path)
.unwrap_or_else(|_| panic!("Failed save {}", path.to_string_lossy()));
}
let path = output_dir.join("final.png");
// Write the contents of this image to the Writer in PNG format.
eprintln!("Saving {}", path.to_string_lossy());
trace!(target: "renderer", "Saving {}", path.to_string_lossy());
img.save(path)
}

View File

@@ -91,6 +91,18 @@ impl Add for Vec3 {
}
}
impl Div<Vec3> for f32 {
type Output = Vec3;
fn div(self, r: Vec3) -> Vec3 {
Vec3 {
x: self / r.x,
y: self / r.y,
z: self / r.z,
}
}
}
impl Div<f32> for Vec3 {
type Output = Vec3;