Implement kd-tree.
Add simple test scene. Failed attempt to make BVH faster. Failed attempt to implement SAH w/ BVH. Failed attempt to make AABB::hit faster.
This commit is contained in:
parent
54f1304695
commit
2104f1a76c
@ -35,6 +35,30 @@ impl AABB {
|
|||||||
AABB { bounds: [min, max] }
|
AABB { bounds: [min, max] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn volume(&self) -> f32 {
|
||||||
|
(self.min().x - self.max().x).abs()
|
||||||
|
* (self.min().y - self.max().y).abs()
|
||||||
|
* (self.min().z - self.max().z).abs()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn longest_axis(&self) -> usize {
|
||||||
|
let xd = (self.min().x - self.max().x).abs();
|
||||||
|
let yd = (self.min().y - self.max().y).abs();
|
||||||
|
let zd = (self.min().z - self.max().z).abs();
|
||||||
|
|
||||||
|
if xd >= yd && xd >= zd {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if yd >= xd && yd >= zd {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mid_point(&self) -> Vec3 {
|
||||||
|
self.bounds[0] + (self.bounds[1] - self.bounds[0]) / 2.
|
||||||
|
}
|
||||||
|
|
||||||
pub fn min(&self) -> Vec3 {
|
pub fn min(&self) -> Vec3 {
|
||||||
self.bounds[0]
|
self.bounds[0]
|
||||||
}
|
}
|
||||||
@ -64,7 +88,7 @@ impl AABB {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit(&self, r: Ray, t0: f32, t1: f32) -> bool {
|
pub fn hit_precompute(&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_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 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_min = (self.bounds[r.sign[0]].y - r.origin.y) * r.inv_direction.y;
|
||||||
@ -95,7 +119,7 @@ impl AABB {
|
|||||||
t_min < t1 && t_max > t0
|
t_min < t1 && t_max > t0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit_original(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
|
pub fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
|
||||||
let mut t_min = t_min;
|
let mut t_min = t_min;
|
||||||
let mut t_max = t_max;
|
let mut t_max = t_max;
|
||||||
for axis in 0..3 {
|
for axis in 0..3 {
|
||||||
|
|||||||
@ -13,9 +13,7 @@ use cpuprofiler::PROFILER;
|
|||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use rtiow::renderer::render;
|
use rtiow::renderer::render;
|
||||||
use rtiow::renderer::Model;
|
|
||||||
use rtiow::renderer::Opt;
|
use rtiow::renderer::Opt;
|
||||||
use rtiow::scenes;
|
|
||||||
|
|
||||||
fn main() -> Result<(), std::io::Error> {
|
fn main() -> Result<(), std::io::Error> {
|
||||||
stderrlog::new()
|
stderrlog::new()
|
||||||
@ -25,14 +23,7 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let opt = Opt::from_args();
|
let opt = Opt::from_args();
|
||||||
let scene = match opt.model {
|
let scene = opt.model.scene(&opt);
|
||||||
Model::Book => scenes::book::new(false, &opt),
|
|
||||||
Model::Bench => scenes::bench::new(&opt),
|
|
||||||
Model::BookBVH => scenes::book::new(true, &opt),
|
|
||||||
Model::Cube => scenes::cube::new(&opt),
|
|
||||||
Model::Tutorial => scenes::tutorial::new(&opt),
|
|
||||||
Model::BVH => scenes::bvh::new(&opt),
|
|
||||||
};
|
|
||||||
fs::create_dir_all(&opt.output)?;
|
fs::create_dir_all(&opt.output)?;
|
||||||
let pprof_path = "pprof.profile";
|
let pprof_path = "pprof.profile";
|
||||||
if opt.pprof {
|
if opt.pprof {
|
||||||
|
|||||||
125
rtiow/src/bvh.rs
125
rtiow/src/bvh.rs
@ -68,16 +68,112 @@ fn box_z_compare(ah: &Box<Hit>, bh: &Box<Hit>) -> std::cmp::Ordering {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BVHNode {
|
|
||||||
fn new(l: Vec<Box<Hit>>, t_min: f32, t_max: f32) -> BVHNode {
|
|
||||||
if l.len() == 1 {
|
|
||||||
// Return the first element from the vector, which should be the only element.
|
// Return the first element from the vector, which should be the only element.
|
||||||
// TODO(wathiede): we really want a .first_into() (.first() doesn't work because it
|
// TODO(wathiede): we really want a .first_into() (.first() doesn't work because it
|
||||||
// returns a reference.)
|
// returns a reference.)
|
||||||
for h in l.into_iter() {
|
fn vec_first_into<T>(v: Vec<T>) -> T {
|
||||||
return BVHNode::Leaf(h);
|
if v.len() != 1 {
|
||||||
|
panic!(format!(
|
||||||
|
"vec_first_into called for vector length != 1, length {}",
|
||||||
|
v.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
for i in v.into_iter() {
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
panic!("Unreachable");
|
panic!("Unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec_split_into<T>(v: Vec<T>, offset: usize) -> (Vec<T>, Vec<T>) {
|
||||||
|
let mut left_half = Vec::new();
|
||||||
|
let mut right_half = Vec::new();
|
||||||
|
v.into_iter().enumerate().for_each(|(i, h)| {
|
||||||
|
if i < offset {
|
||||||
|
left_half.push(h);
|
||||||
|
} else {
|
||||||
|
right_half.push(h);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(left_half, right_half)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BVHNode {
|
||||||
|
fn new_sah(l: Vec<Box<Hit>>, t_min: f32, t_max: f32) -> BVHNode {
|
||||||
|
eprintln!("l.len() {}", l.len());
|
||||||
|
let mut l: Vec<Box<Hit>> = l.into_iter().collect();
|
||||||
|
let n = l.len();
|
||||||
|
let main_box =
|
||||||
|
l[1..]
|
||||||
|
.iter()
|
||||||
|
.fold(l[0].bounding_box(t_min, t_max).unwrap(), |acc, h| {
|
||||||
|
match h.bounding_box(t_min, t_max) {
|
||||||
|
Some(new_box) => surrounding_box(&new_box, &acc),
|
||||||
|
None => panic!("hit without bounding box"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let axis = main_box.longest_axis();
|
||||||
|
match axis {
|
||||||
|
0 => l.sort_by(box_x_compare),
|
||||||
|
1 => l.sort_by(box_y_compare),
|
||||||
|
2 => l.sort_by(box_z_compare),
|
||||||
|
val @ _ => panic!("unknown axis {}", val),
|
||||||
|
};
|
||||||
|
let boxes: Vec<_> = l
|
||||||
|
.iter()
|
||||||
|
.map(|hit| hit.bounding_box(t_min, t_max).unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut left_area = Vec::with_capacity(n);
|
||||||
|
let mut left_box = boxes[0].clone();
|
||||||
|
for i in 1..n - 1 {
|
||||||
|
left_box = surrounding_box(&left_box, &boxes[i]);
|
||||||
|
left_area[i] = left_box.volume();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut right_area = Vec::with_capacity(n);
|
||||||
|
right_area[n - 1] = boxes[n - 1].volume();
|
||||||
|
let mut right_box = boxes[n - 1].clone();
|
||||||
|
for i in (0..n - 2).rev() {
|
||||||
|
right_box = surrounding_box(&right_box, &boxes[i]);
|
||||||
|
right_area[i] = right_box.volume();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut min_sah = std::f32::MAX;
|
||||||
|
let mut min_sah_idx = 0;
|
||||||
|
for i in 0..n - 1 {
|
||||||
|
let sah = i as f32 * left_area[i] + (n - i - 1) as f32 * right_area[i + 1];
|
||||||
|
if sah < min_sah {
|
||||||
|
min_sah_idx = i;
|
||||||
|
min_sah = sah;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let left: Box<BVHNode>;
|
||||||
|
let right: Box<BVHNode>;
|
||||||
|
if min_sah_idx == 0 {
|
||||||
|
let (l, r) = vec_split_into(l, 1);
|
||||||
|
left = Box::new(BVHNode::Leaf(vec_first_into(l)));
|
||||||
|
right = Box::new(BVHNode::new_sah(r, t_min, t_max));
|
||||||
|
} else if min_sah_idx == n - 2 {
|
||||||
|
let (l, r) = vec_split_into(l, min_sah_idx + 1);
|
||||||
|
left = Box::new(BVHNode::new_sah(l, t_min, t_max));
|
||||||
|
right = Box::new(BVHNode::Leaf(vec_first_into(r)));
|
||||||
|
} else {
|
||||||
|
let (l, r) = vec_split_into(l, min_sah_idx + 1);
|
||||||
|
left = Box::new(BVHNode::new_sah(l, t_min, t_max));
|
||||||
|
right = Box::new(BVHNode::new_sah(r, t_min, t_max));
|
||||||
|
}
|
||||||
|
|
||||||
|
BVHNode::Branch {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
bbox: Some(main_box),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn new_dumb(l: Vec<Box<Hit>>, t_min: f32, t_max: f32) -> BVHNode {
|
||||||
|
if l.len() == 1 {
|
||||||
|
return BVHNode::Leaf(vec_first_into(l));
|
||||||
} else {
|
} else {
|
||||||
let mut l: Vec<Box<Hit>> = l.into_iter().collect();
|
let mut l: Vec<Box<Hit>> = l.into_iter().collect();
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
@ -88,18 +184,10 @@ impl BVHNode {
|
|||||||
val @ _ => panic!("unknown axis {}", val),
|
val @ _ => panic!("unknown axis {}", val),
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut left_half = Vec::new();
|
let half = l.len() / 2;
|
||||||
let mut right_half = Vec::new();
|
let (left_half, right_half) = vec_split_into(l, half);
|
||||||
let half_idx = l.len() / 2;
|
let left = Box::new(BVHNode::new_dumb(left_half, t_min, t_max));
|
||||||
l.into_iter().enumerate().for_each(|(i, h)| {
|
let right = Box::new(BVHNode::new_dumb(right_half, t_min, t_max));
|
||||||
if i < half_idx {
|
|
||||||
left_half.push(h);
|
|
||||||
} else {
|
|
||||||
right_half.push(h);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let left = Box::new(BVHNode::new(left_half, t_min, t_max));
|
|
||||||
let right = Box::new(BVHNode::new(right_half, t_min, t_max));
|
|
||||||
let bbox = BVHNode::surrounding_box(left.as_ref(), right.as_ref(), t_min, t_max);
|
let bbox = BVHNode::surrounding_box(left.as_ref(), right.as_ref(), t_min, t_max);
|
||||||
return BVHNode::Branch { left, right, bbox };
|
return BVHNode::Branch { left, right, bbox };
|
||||||
}
|
}
|
||||||
@ -129,6 +217,7 @@ impl Hit for BVHNode {
|
|||||||
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
||||||
match self {
|
match self {
|
||||||
BVHNode::Leaf(ref hit) => hit.hit(r, t_min, t_max),
|
BVHNode::Leaf(ref hit) => hit.hit(r, t_min, t_max),
|
||||||
|
// TODO(wathiede): call bbox.hit() before recursing down both paths of tree.
|
||||||
BVHNode::Branch {
|
BVHNode::Branch {
|
||||||
ref left,
|
ref left,
|
||||||
ref right,
|
ref right,
|
||||||
@ -163,7 +252,7 @@ impl BVH {
|
|||||||
let count = l.len();
|
let count = l.len();
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let bvh = BVH {
|
let bvh = BVH {
|
||||||
root: BVHNode::new(l, t_min, t_max),
|
root: BVHNode::new_sah(l, t_min, t_max),
|
||||||
};
|
};
|
||||||
let runtime = start.elapsed();
|
let runtime = start.elapsed();
|
||||||
info!(
|
info!(
|
||||||
|
|||||||
@ -25,6 +25,8 @@ impl Cube {
|
|||||||
|
|
||||||
impl Hit for Cube {
|
impl Hit for Cube {
|
||||||
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
||||||
|
info!("Cube info");
|
||||||
|
eprintln!("Cube eprintln");
|
||||||
// See if it hit the positive X wall first.
|
// See if it hit the positive X wall first.
|
||||||
let oc = r.origin - self.center;
|
let oc = r.origin - self.center;
|
||||||
let a = dot(r.direction, r.direction);
|
let a = dot(r.direction, r.direction);
|
||||||
|
|||||||
188
rtiow/src/kdtree.rs
Normal file
188
rtiow/src/kdtree.rs
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use aabb::surrounding_box;
|
||||||
|
use aabb::AABB;
|
||||||
|
use hitable::Hit;
|
||||||
|
use hitable::HitRecord;
|
||||||
|
use ray::Ray;
|
||||||
|
|
||||||
|
pub enum KDTree {
|
||||||
|
Leaf(Box<Hit>),
|
||||||
|
Branch {
|
||||||
|
left: Box<KDTree>,
|
||||||
|
right: Box<KDTree>,
|
||||||
|
bbox: Option<AABB>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the first element from the vector, which should be the only element.
|
||||||
|
// TODO(wathiede): we really want a .first_into() (.first() doesn't work because it
|
||||||
|
// returns a reference.)
|
||||||
|
fn vec_first_into<T>(v: Vec<T>) -> T {
|
||||||
|
if v.len() != 1 {
|
||||||
|
panic!(format!(
|
||||||
|
"vec_first_into called for vector length != 1, length {}",
|
||||||
|
v.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
for i in v.into_iter() {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
panic!("Unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec_split_into<T>(v: Vec<T>, offset: usize) -> (Vec<T>, Vec<T>) {
|
||||||
|
let mut left_half = Vec::new();
|
||||||
|
let mut right_half = Vec::new();
|
||||||
|
v.into_iter().enumerate().for_each(|(i, h)| {
|
||||||
|
if i < offset {
|
||||||
|
left_half.push(h);
|
||||||
|
} else {
|
||||||
|
right_half.push(h);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(left_half, right_half)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KDTree {
|
||||||
|
pub fn new(l: Vec<Box<Hit>>, t_min: f32, t_max: f32) -> KDTree {
|
||||||
|
if l.len() == 0 {
|
||||||
|
panic!("Attempt to build k-d tree with no Hit objects");
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.len() == 1 {
|
||||||
|
return KDTree::Leaf(vec_first_into(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.len() == 2 {
|
||||||
|
let (left_vec, right_vec) = vec_split_into(l, 1);
|
||||||
|
let left = vec_first_into(left_vec);
|
||||||
|
let right = vec_first_into(right_vec);
|
||||||
|
let bbox = surrounding_box(
|
||||||
|
&left.bounding_box(t_min, t_max).unwrap(),
|
||||||
|
&right.bounding_box(t_min, t_max).unwrap(),
|
||||||
|
);
|
||||||
|
return KDTree::Branch {
|
||||||
|
left: Box::new(KDTree::Leaf(left)),
|
||||||
|
right: Box::new(KDTree::Leaf(right)),
|
||||||
|
bbox: Some(bbox),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let bbox = l[0].bounding_box(t_min, t_max).unwrap();
|
||||||
|
let mut mid_point = bbox.mid_point();
|
||||||
|
let bbox = l[1..].iter().fold(bbox, |acc, h| {
|
||||||
|
let new_bbox = &h.bounding_box(t_min, t_max).unwrap();
|
||||||
|
mid_point = mid_point + new_bbox.mid_point();
|
||||||
|
surrounding_box(&acc, new_bbox)
|
||||||
|
});
|
||||||
|
mid_point = mid_point / l.len() as f32;
|
||||||
|
let axis = bbox.longest_axis();
|
||||||
|
let mut left_half = Vec::new();
|
||||||
|
let mut right_half = Vec::new();
|
||||||
|
match axis {
|
||||||
|
0 => l.into_iter().for_each(|h| {
|
||||||
|
if h.bounding_box(t_min, t_max).unwrap().mid_point().x < mid_point.x {
|
||||||
|
left_half.push(h);
|
||||||
|
} else {
|
||||||
|
right_half.push(h);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
1 => l.into_iter().for_each(|h| {
|
||||||
|
if h.bounding_box(t_min, t_max).unwrap().mid_point().y < mid_point.y {
|
||||||
|
left_half.push(h);
|
||||||
|
} else {
|
||||||
|
right_half.push(h);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
2 => l.into_iter().for_each(|h| {
|
||||||
|
if h.bounding_box(t_min, t_max).unwrap().mid_point().z < mid_point.z {
|
||||||
|
left_half.push(h);
|
||||||
|
} else {
|
||||||
|
right_half.push(h);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_ => panic!("Unreachable"),
|
||||||
|
};
|
||||||
|
KDTree::Branch {
|
||||||
|
left: Box::new(KDTree::new(left_half, t_min, t_max)),
|
||||||
|
right: Box::new(KDTree::new(right_half, t_min, t_max)),
|
||||||
|
bbox: Some(bbox),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_tree(f: &mut fmt::Formatter, depth: usize, kdt: &KDTree) -> fmt::Result {
|
||||||
|
let vol = kdt.volume();
|
||||||
|
write!(f, "{:8.2}{}", vol, " ".repeat(depth * 2))?;
|
||||||
|
match kdt {
|
||||||
|
KDTree::Leaf(ref hit) => write!(
|
||||||
|
f,
|
||||||
|
"Leaf: {}\n",
|
||||||
|
hit.bounding_box(0., 0.)
|
||||||
|
.map_or("NO BBOX".to_owned(), |bb| bb.to_string())
|
||||||
|
)?,
|
||||||
|
KDTree::Branch { bbox, .. } => write!(
|
||||||
|
f,
|
||||||
|
"Branch: {}\n",
|
||||||
|
// TODO(wathiede): removing this .clone() results in a complaint about moving out
|
||||||
|
// of a borrow.
|
||||||
|
bbox.clone()
|
||||||
|
.map_or("NO BBOX".to_owned(), |bb| bb.to_string())
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
if let KDTree::Branch { left, right, .. } = kdt {
|
||||||
|
print_tree(f, depth + 1, left)?;
|
||||||
|
print_tree(f, depth + 1, right)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for KDTree {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "kd-tree\n")?;
|
||||||
|
print_tree(f, 1, &self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Hit for KDTree {
|
||||||
|
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
||||||
|
match self.bounding_box(t_min, t_max) {
|
||||||
|
Some(bbox) => if !bbox.hit(r, t_min, t_max) {
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
info!("KDTree::hit no bbox: {:?}", r);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
KDTree::Leaf(ref hit) => hit.hit(r, t_min, t_max),
|
||||||
|
KDTree::Branch { left, right, bbox } => match bbox {
|
||||||
|
None => None,
|
||||||
|
Some(_bbox) => match (left.hit(r, t_min, t_max), right.hit(r, t_min, t_max)) {
|
||||||
|
(Some(hit_left), Some(hit_right)) => if hit_left.t < hit_right.t {
|
||||||
|
return Some(hit_left);
|
||||||
|
} else {
|
||||||
|
return Some(hit_right);
|
||||||
|
},
|
||||||
|
(Some(hit_left), None) => Some(hit_left),
|
||||||
|
(None, Some(hit_right)) => Some(hit_right),
|
||||||
|
(None, None) => None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, t_min: f32, t_max: f32) -> Option<AABB> {
|
||||||
|
match self {
|
||||||
|
KDTree::Leaf(ref hit) => hit.bounding_box(t_min, t_max),
|
||||||
|
KDTree::Branch { bbox, .. } => bbox.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ pub mod camera;
|
|||||||
pub mod cube;
|
pub mod cube;
|
||||||
pub mod hitable;
|
pub mod hitable;
|
||||||
pub mod hitable_list;
|
pub mod hitable_list;
|
||||||
|
pub mod kdtree;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
pub mod moving_sphere;
|
pub mod moving_sphere;
|
||||||
pub mod ray;
|
pub mod ray;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use vec3::Vec3;
|
use vec3::Vec3;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
pub origin: Vec3,
|
pub origin: Vec3,
|
||||||
pub direction: Vec3,
|
pub direction: Vec3,
|
||||||
|
|||||||
@ -16,16 +16,30 @@ use rand::Rng;
|
|||||||
use camera::Camera;
|
use camera::Camera;
|
||||||
use hitable::Hit;
|
use hitable::Hit;
|
||||||
use ray::Ray;
|
use ray::Ray;
|
||||||
|
use scenes;
|
||||||
use vec3::Vec3;
|
use vec3::Vec3;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Model {
|
pub enum Model {
|
||||||
Bench,
|
Bench,
|
||||||
Book,
|
Book,
|
||||||
BookBVH,
|
|
||||||
Tutorial,
|
Tutorial,
|
||||||
Cube,
|
Cube,
|
||||||
BVH,
|
BVH,
|
||||||
|
Test,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Model {
|
||||||
|
pub fn scene(&self, opt: &Opt) -> Scene {
|
||||||
|
match self {
|
||||||
|
Model::Book => scenes::book::new(&opt),
|
||||||
|
Model::Bench => scenes::bench::new(&opt),
|
||||||
|
Model::Cube => scenes::cube::new(&opt),
|
||||||
|
Model::Tutorial => scenes::tutorial::new(&opt),
|
||||||
|
Model::BVH => scenes::bvh::new(&opt),
|
||||||
|
Model::Test => scenes::test::new(&opt),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -43,10 +57,10 @@ impl str::FromStr for Model {
|
|||||||
match s {
|
match s {
|
||||||
"bench" => Ok(Model::Bench),
|
"bench" => Ok(Model::Bench),
|
||||||
"book" => Ok(Model::Book),
|
"book" => Ok(Model::Book),
|
||||||
"book_bvh" => Ok(Model::BookBVH),
|
|
||||||
"tutorial" => Ok(Model::Tutorial),
|
"tutorial" => Ok(Model::Tutorial),
|
||||||
"cube" => Ok(Model::Cube),
|
"cube" => Ok(Model::Cube),
|
||||||
"bvh" => Ok(Model::BVH),
|
"bvh" => Ok(Model::BVH),
|
||||||
|
"test" => Ok(Model::Test),
|
||||||
_ => Err(ModelParseError(s.to_owned())),
|
_ => Err(ModelParseError(s.to_owned())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,18 +70,21 @@ impl str::FromStr for Model {
|
|||||||
#[structopt(name = "tracer", about = "An experimental ray tracer.")]
|
#[structopt(name = "tracer", about = "An experimental ray tracer.")]
|
||||||
pub struct Opt {
|
pub struct Opt {
|
||||||
/// Image width
|
/// Image width
|
||||||
#[structopt(short = "w", long = "width", default_value = "128")]
|
#[structopt(short = "w", long = "width", default_value = "1024")]
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
/// Image height
|
/// Image height
|
||||||
#[structopt(short = "h", long = "height", default_value = "128")]
|
#[structopt(short = "h", long = "height", default_value = "1024")]
|
||||||
pub height: usize,
|
pub height: usize,
|
||||||
/// Sub-samples per pixel
|
/// Sub-samples per pixel
|
||||||
#[structopt(short = "s", long = "subsample", default_value = "1")]
|
#[structopt(short = "s", long = "subsample", default_value = "1")]
|
||||||
pub subsamples: usize,
|
pub subsamples: usize,
|
||||||
#[structopt(long = "model", default_value = "bench")]
|
#[structopt(long = "model", default_value = "test")]
|
||||||
pub model: Model,
|
pub model: Model,
|
||||||
#[structopt(long = "pprof")]
|
#[structopt(long = "pprof")]
|
||||||
pub pprof: bool,
|
pub pprof: bool,
|
||||||
|
/// Use acceleration data structure, may be BVH or kd-tree depending on scene.
|
||||||
|
#[structopt(long = "use_accel")]
|
||||||
|
pub use_accel: bool,
|
||||||
|
|
||||||
/// Output directory
|
/// Output directory
|
||||||
#[structopt(parse(from_os_str), default_value = "/tmp/tracer")]
|
#[structopt(parse(from_os_str), default_value = "/tmp/tracer")]
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
use rand;
|
use rand;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use bvh::BVH;
|
|
||||||
use camera::Camera;
|
use camera::Camera;
|
||||||
use hitable::Hit;
|
use hitable::Hit;
|
||||||
use hitable_list::HitableList;
|
use hitable_list::HitableList;
|
||||||
|
use kdtree::KDTree;
|
||||||
use material::Lambertian;
|
use material::Lambertian;
|
||||||
use renderer::Opt;
|
use renderer::Opt;
|
||||||
use renderer::Scene;
|
use renderer::Scene;
|
||||||
@ -12,7 +12,7 @@ use sphere::Sphere;
|
|||||||
use vec3::Vec3;
|
use vec3::Vec3;
|
||||||
|
|
||||||
pub fn new(opt: &Opt) -> Scene {
|
pub fn new(opt: &Opt) -> Scene {
|
||||||
let lookfrom = Vec3::new(0.01, 20., 0.);
|
let lookfrom = Vec3::new(20., 20., 20.);
|
||||||
let lookat = Vec3::new(0., 0., 0.);
|
let lookat = Vec3::new(0., 0., 0.);
|
||||||
let dist_to_focus = (lookfrom - lookat).length();
|
let dist_to_focus = (lookfrom - lookat).length();
|
||||||
let aperture = 0.1;
|
let aperture = 0.1;
|
||||||
@ -31,7 +31,7 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
);
|
);
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut grid: Vec<Box<Hit>> = Vec::new();
|
let mut grid: Vec<Box<Hit>> = Vec::new();
|
||||||
let len = 30;
|
let len = 100;
|
||||||
for x in 0..len {
|
for x in 0..len {
|
||||||
for z in 0..len {
|
for z in 0..len {
|
||||||
let r = rng.gen_range::<f32>(0., 1.);
|
let r = rng.gen_range::<f32>(0., 1.);
|
||||||
@ -45,12 +45,12 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let use_bvh = false;
|
let use_kdtree = true;
|
||||||
let world: Box<Hit>;
|
let world: Box<Hit>;
|
||||||
if use_bvh {
|
if use_kdtree {
|
||||||
let bvh = BVH::new(grid, time_min, time_max);
|
let kdtree = KDTree::new(grid, time_min, time_max);
|
||||||
trace!(target: "bvh", "World {}", bvh);
|
//trace!(target: "k-d tree", "World {}", kdtree);
|
||||||
world = Box::new(bvh);
|
world = Box::new(kdtree);
|
||||||
} else {
|
} else {
|
||||||
world = Box::new(HitableList::new(grid));
|
world = Box::new(HitableList::new(grid));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use bvh::BVH;
|
|||||||
use camera::Camera;
|
use camera::Camera;
|
||||||
use hitable::Hit;
|
use hitable::Hit;
|
||||||
use hitable_list::HitableList;
|
use hitable_list::HitableList;
|
||||||
|
use kdtree::KDTree;
|
||||||
use material::Dielectric;
|
use material::Dielectric;
|
||||||
use material::Lambertian;
|
use material::Lambertian;
|
||||||
use material::Metal;
|
use material::Metal;
|
||||||
@ -13,7 +14,7 @@ use renderer::Scene;
|
|||||||
use sphere::Sphere;
|
use sphere::Sphere;
|
||||||
use vec3::Vec3;
|
use vec3::Vec3;
|
||||||
|
|
||||||
pub fn new(bvh: bool, opt: &Opt) -> Scene {
|
pub fn new(opt: &Opt) -> Scene {
|
||||||
let lookfrom = Vec3::new(13., 2., 3.);
|
let lookfrom = Vec3::new(13., 2., 3.);
|
||||||
let lookat = Vec3::new(0., 0., 0.);
|
let lookat = Vec3::new(0., 0., 0.);
|
||||||
let dist_to_focus = 10.;
|
let dist_to_focus = 10.;
|
||||||
@ -31,14 +32,16 @@ pub fn new(bvh: bool, opt: &Opt) -> Scene {
|
|||||||
time_min,
|
time_min,
|
||||||
time_max,
|
time_max,
|
||||||
);
|
);
|
||||||
let world: Box<Hit>;
|
let ground_color = if opt.use_accel {
|
||||||
if bvh {
|
Vec3::new(1.0, 0.4, 0.4)
|
||||||
let b = BVH::new(random_scene(), time_min, time_max);
|
|
||||||
trace!(target: "bvh", "World {}", b);
|
|
||||||
world = Box::new(b);
|
|
||||||
} else {
|
} else {
|
||||||
world = Box::new(HitableList::new(random_scene()));
|
Vec3::new(0.4, 1.0, 0.4)
|
||||||
}
|
};
|
||||||
|
let world: Box<Hit> = if opt.use_accel {
|
||||||
|
Box::new(KDTree::new(random_scene(ground_color), time_min, time_max))
|
||||||
|
} else {
|
||||||
|
Box::new(HitableList::new(random_scene(ground_color)))
|
||||||
|
};
|
||||||
Scene {
|
Scene {
|
||||||
camera,
|
camera,
|
||||||
world,
|
world,
|
||||||
@ -48,12 +51,12 @@ pub fn new(bvh: bool, opt: &Opt) -> Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_scene() -> Vec<Box<Hit>> {
|
fn random_scene(background_color: Vec3) -> Vec<Box<Hit>> {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut objects: Vec<Box<Hit>> = vec![Box::new(Sphere::new(
|
let mut objects: Vec<Box<Hit>> = vec![Box::new(Sphere::new(
|
||||||
Vec3::new(0., -1000., 0.),
|
Vec3::new(0., -1000., 0.),
|
||||||
1000.,
|
1000.,
|
||||||
Box::new(Lambertian::new(Vec3::new(0.5, 0.5, 0.5))),
|
Box::new(Lambertian::new(background_color)),
|
||||||
))];
|
))];
|
||||||
let mut random = || rng.gen_range::<f32>(0., 1.);
|
let mut random = || rng.gen_range::<f32>(0., 1.);
|
||||||
|
|
||||||
|
|||||||
@ -2,4 +2,5 @@ pub mod bench;
|
|||||||
pub mod book;
|
pub mod book;
|
||||||
pub mod bvh;
|
pub mod bvh;
|
||||||
pub mod cube;
|
pub mod cube;
|
||||||
|
pub mod test;
|
||||||
pub mod tutorial;
|
pub mod tutorial;
|
||||||
|
|||||||
80
rtiow/src/scenes/test.rs
Normal file
80
rtiow/src/scenes/test.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
use rand;
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use camera::Camera;
|
||||||
|
use hitable::Hit;
|
||||||
|
use hitable_list::HitableList;
|
||||||
|
use kdtree::KDTree;
|
||||||
|
use material::Lambertian;
|
||||||
|
use material::Metal;
|
||||||
|
use renderer::Opt;
|
||||||
|
use renderer::Scene;
|
||||||
|
use sphere::Sphere;
|
||||||
|
use vec3::Vec3;
|
||||||
|
|
||||||
|
pub fn new(opt: &Opt) -> Scene {
|
||||||
|
let lookfrom = Vec3::new(0.001, 0.001, 20.);
|
||||||
|
let lookat = Vec3::new(0., 0., 0.);
|
||||||
|
let dist_to_focus = (lookfrom - lookat).length();
|
||||||
|
let aperture = 0.1;
|
||||||
|
let time_min = 0.;
|
||||||
|
let time_max = 0.;
|
||||||
|
let camera = Camera::new(
|
||||||
|
lookfrom,
|
||||||
|
lookat,
|
||||||
|
Vec3::new(0., 1., 0.),
|
||||||
|
45.,
|
||||||
|
opt.width as f32 / opt.height as f32,
|
||||||
|
aperture,
|
||||||
|
dist_to_focus,
|
||||||
|
time_min,
|
||||||
|
time_max,
|
||||||
|
);
|
||||||
|
let mut objects: Vec<Box<Hit>> = vec![Box::new(Sphere::new(
|
||||||
|
Vec3::new(0., 0., 0.),
|
||||||
|
5.,
|
||||||
|
Box::new(Metal::new(Vec3::new(0.5, 0.5, 0.5), 0.)),
|
||||||
|
))];
|
||||||
|
let num_spheres = 6;
|
||||||
|
let radius = 7.;
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut random = || rng.gen_range::<f32>(0., 1.);
|
||||||
|
for i in 0..num_spheres {
|
||||||
|
let c1 = radius * ((2. * PI) * (i as f32 / num_spheres as f32)).sin();
|
||||||
|
let c2 = radius * ((2. * PI) * (i as f32 / num_spheres as f32)).cos();
|
||||||
|
let center = Vec3::new(0., c1, c2);
|
||||||
|
objects.push(Box::new(Sphere::new(
|
||||||
|
center,
|
||||||
|
0.5,
|
||||||
|
Box::new(Lambertian::new(Vec3::new(1., 0., 0.))),
|
||||||
|
)));
|
||||||
|
let center = Vec3::new(c1, 0., c2);
|
||||||
|
objects.push(Box::new(Sphere::new(
|
||||||
|
center,
|
||||||
|
0.5,
|
||||||
|
Box::new(Lambertian::new(Vec3::new(0., 1., 0.))),
|
||||||
|
)));
|
||||||
|
let center = Vec3::new(c1, c2, 0.);
|
||||||
|
objects.push(Box::new(Sphere::new(
|
||||||
|
center,
|
||||||
|
0.5,
|
||||||
|
Box::new(Lambertian::new(Vec3::new(0., 0., 1.))),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let world: Box<Hit> = if opt.use_accel {
|
||||||
|
let kd = KDTree::new(objects, time_min, time_max);
|
||||||
|
//println!("{}", kd);
|
||||||
|
Box::new(kd)
|
||||||
|
} else {
|
||||||
|
Box::new(HitableList::new(objects))
|
||||||
|
};
|
||||||
|
Scene {
|
||||||
|
camera,
|
||||||
|
world,
|
||||||
|
subsamples: opt.subsamples,
|
||||||
|
width: opt.width,
|
||||||
|
height: opt.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
use camera::Camera;
|
use camera::Camera;
|
||||||
use hitable::Hit;
|
use hitable::Hit;
|
||||||
use hitable_list::HitableList;
|
use hitable_list::HitableList;
|
||||||
|
use kdtree::KDTree;
|
||||||
use material::Lambertian;
|
use material::Lambertian;
|
||||||
use material::Metal;
|
use material::Metal;
|
||||||
use moving_sphere::MovingSphere;
|
use moving_sphere::MovingSphere;
|
||||||
@ -15,7 +16,7 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
let dist_to_focus = (lookfrom - lookat).length();
|
let dist_to_focus = (lookfrom - lookat).length();
|
||||||
let aperture = 0.1;
|
let aperture = 0.1;
|
||||||
let time_min = 0.;
|
let time_min = 0.;
|
||||||
let time_max = 1.;
|
let time_max = 0.;
|
||||||
let camera = Camera::new(
|
let camera = Camera::new(
|
||||||
lookfrom,
|
lookfrom,
|
||||||
lookat,
|
lookat,
|
||||||
@ -27,7 +28,14 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
time_min,
|
time_min,
|
||||||
time_max,
|
time_max,
|
||||||
);
|
);
|
||||||
let world: Box<Hit> = Box::new(HitableList::new(vec![
|
let ground_color = if opt.use_accel {
|
||||||
|
Vec3::new(1.0, 0.4, 0.4)
|
||||||
|
} else {
|
||||||
|
Vec3::new(0.4, 1.0, 0.4)
|
||||||
|
};
|
||||||
|
|
||||||
|
let objects: Vec<Box<Hit>> = vec![
|
||||||
|
//let world: Box<Hit> = Box::new(HitableList::new(vec![
|
||||||
Box::new(Sphere::new(
|
Box::new(Sphere::new(
|
||||||
Vec3::new(0., 0., -1.),
|
Vec3::new(0., 0., -1.),
|
||||||
0.5,
|
0.5,
|
||||||
@ -36,7 +44,7 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
Box::new(Sphere::new(
|
Box::new(Sphere::new(
|
||||||
Vec3::new(0., -100.5, -1.),
|
Vec3::new(0., -100.5, -1.),
|
||||||
100.,
|
100.,
|
||||||
Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.8))),
|
Box::new(Lambertian::new(ground_color)),
|
||||||
)),
|
)),
|
||||||
Box::new(Sphere::new(
|
Box::new(Sphere::new(
|
||||||
Vec3::new(1., 0., -1.),
|
Vec3::new(1., 0., -1.),
|
||||||
@ -51,7 +59,12 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
1.,
|
1.,
|
||||||
Box::new(Lambertian::new(Vec3::new(0.2, 0.8, 0.2))),
|
Box::new(Lambertian::new(Vec3::new(0.2, 0.8, 0.2))),
|
||||||
)),
|
)),
|
||||||
]));
|
];
|
||||||
|
let world: Box<Hit> = if opt.use_accel {
|
||||||
|
Box::new(KDTree::new(objects, time_min, time_max))
|
||||||
|
} else {
|
||||||
|
Box::new(HitableList::new(objects))
|
||||||
|
};
|
||||||
Scene {
|
Scene {
|
||||||
camera,
|
camera,
|
||||||
world,
|
world,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user