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:
Bill Thiede 2018-09-22 11:10:09 -07:00
parent 54f1304695
commit 2104f1a76c
13 changed files with 470 additions and 61 deletions

View File

@ -35,6 +35,30 @@ impl AABB {
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 {
self.bounds[0]
}
@ -64,7 +88,7 @@ impl AABB {
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_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;
@ -95,7 +119,7 @@ impl AABB {
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_max = t_max;
for axis in 0..3 {

View File

@ -13,9 +13,7 @@ use cpuprofiler::PROFILER;
use structopt::StructOpt;
use rtiow::renderer::render;
use rtiow::renderer::Model;
use rtiow::renderer::Opt;
use rtiow::scenes;
fn main() -> Result<(), std::io::Error> {
stderrlog::new()
@ -25,14 +23,7 @@ fn main() -> Result<(), std::io::Error> {
.unwrap();
let start = Instant::now();
let opt = Opt::from_args();
let scene = match opt.model {
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),
};
let scene = opt.model.scene(&opt);
fs::create_dir_all(&opt.output)?;
let pprof_path = "pprof.profile";
if opt.pprof {

View File

@ -68,16 +68,112 @@ fn box_z_compare(ah: &Box<Hit>, bh: &Box<Hit>) -> std::cmp::Ordering {
}
}
// 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 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.
// TODO(wathiede): we really want a .first_into() (.first() doesn't work because it
// returns a reference.)
for h in l.into_iter() {
return BVHNode::Leaf(h);
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;
}
panic!("Unreachable");
}
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 {
let mut l: Vec<Box<Hit>> = l.into_iter().collect();
let mut rng = rand::thread_rng();
@ -88,18 +184,10 @@ impl BVHNode {
val @ _ => panic!("unknown axis {}", val),
}
let mut left_half = Vec::new();
let mut right_half = Vec::new();
let half_idx = l.len() / 2;
l.into_iter().enumerate().for_each(|(i, h)| {
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 half = l.len() / 2;
let (left_half, right_half) = vec_split_into(l, half);
let left = Box::new(BVHNode::new_dumb(left_half, t_min, t_max));
let right = Box::new(BVHNode::new_dumb(right_half, 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 };
}
@ -129,6 +217,7 @@ impl Hit for BVHNode {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
match self {
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 {
ref left,
ref right,
@ -163,7 +252,7 @@ impl BVH {
let count = l.len();
let start = Instant::now();
let bvh = BVH {
root: BVHNode::new(l, t_min, t_max),
root: BVHNode::new_sah(l, t_min, t_max),
};
let runtime = start.elapsed();
info!(

View File

@ -25,6 +25,8 @@ impl Cube {
impl Hit for Cube {
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.
let oc = r.origin - self.center;
let a = dot(r.direction, r.direction);

188
rtiow/src/kdtree.rs Normal file
View 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(),
}
}
}

View File

@ -4,6 +4,7 @@ pub mod camera;
pub mod cube;
pub mod hitable;
pub mod hitable_list;
pub mod kdtree;
pub mod material;
pub mod moving_sphere;
pub mod ray;

View File

@ -1,6 +1,6 @@
use vec3::Vec3;
#[derive(Copy, Clone, Default)]
#[derive(Copy, Clone, Debug, Default)]
pub struct Ray {
pub origin: Vec3,
pub direction: Vec3,

View File

@ -16,16 +16,30 @@ use rand::Rng;
use camera::Camera;
use hitable::Hit;
use ray::Ray;
use scenes;
use vec3::Vec3;
#[derive(Debug)]
pub enum Model {
Bench,
Book,
BookBVH,
Tutorial,
Cube,
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)]
@ -43,10 +57,10 @@ impl str::FromStr for Model {
match s {
"bench" => Ok(Model::Bench),
"book" => Ok(Model::Book),
"book_bvh" => Ok(Model::BookBVH),
"tutorial" => Ok(Model::Tutorial),
"cube" => Ok(Model::Cube),
"bvh" => Ok(Model::BVH),
"test" => Ok(Model::Test),
_ => Err(ModelParseError(s.to_owned())),
}
}
@ -56,18 +70,21 @@ impl str::FromStr for Model {
#[structopt(name = "tracer", about = "An experimental ray tracer.")]
pub struct Opt {
/// Image width
#[structopt(short = "w", long = "width", default_value = "128")]
#[structopt(short = "w", long = "width", default_value = "1024")]
pub width: usize,
/// Image height
#[structopt(short = "h", long = "height", default_value = "128")]
#[structopt(short = "h", long = "height", default_value = "1024")]
pub height: usize,
/// Sub-samples per pixel
#[structopt(short = "s", long = "subsample", default_value = "1")]
pub subsamples: usize,
#[structopt(long = "model", default_value = "bench")]
#[structopt(long = "model", default_value = "test")]
pub model: Model,
#[structopt(long = "pprof")]
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
#[structopt(parse(from_os_str), default_value = "/tmp/tracer")]

View File

@ -1,10 +1,10 @@
use rand;
use rand::Rng;
use bvh::BVH;
use camera::Camera;
use hitable::Hit;
use hitable_list::HitableList;
use kdtree::KDTree;
use material::Lambertian;
use renderer::Opt;
use renderer::Scene;
@ -12,7 +12,7 @@ use sphere::Sphere;
use vec3::Vec3;
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 dist_to_focus = (lookfrom - lookat).length();
let aperture = 0.1;
@ -31,7 +31,7 @@ pub fn new(opt: &Opt) -> Scene {
);
let mut rng = rand::thread_rng();
let mut grid: Vec<Box<Hit>> = Vec::new();
let len = 30;
let len = 100;
for x in 0..len {
for z in 0..len {
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>;
if use_bvh {
let bvh = BVH::new(grid, time_min, time_max);
trace!(target: "bvh", "World {}", bvh);
world = Box::new(bvh);
if use_kdtree {
let kdtree = KDTree::new(grid, time_min, time_max);
//trace!(target: "k-d tree", "World {}", kdtree);
world = Box::new(kdtree);
} else {
world = Box::new(HitableList::new(grid));
}

View File

@ -5,6 +5,7 @@ use bvh::BVH;
use camera::Camera;
use hitable::Hit;
use hitable_list::HitableList;
use kdtree::KDTree;
use material::Dielectric;
use material::Lambertian;
use material::Metal;
@ -13,7 +14,7 @@ use renderer::Scene;
use sphere::Sphere;
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 lookat = Vec3::new(0., 0., 0.);
let dist_to_focus = 10.;
@ -31,14 +32,16 @@ pub fn new(bvh: bool, opt: &Opt) -> Scene {
time_min,
time_max,
);
let world: Box<Hit>;
if bvh {
let b = BVH::new(random_scene(), time_min, time_max);
trace!(target: "bvh", "World {}", b);
world = Box::new(b);
let ground_color = if opt.use_accel {
Vec3::new(1.0, 0.4, 0.4)
} 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 {
camera,
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 objects: Vec<Box<Hit>> = vec![Box::new(Sphere::new(
Vec3::new(0., -1000., 0.),
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.);

View File

@ -2,4 +2,5 @@ pub mod bench;
pub mod book;
pub mod bvh;
pub mod cube;
pub mod test;
pub mod tutorial;

80
rtiow/src/scenes/test.rs Normal file
View 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,
}
}

View File

@ -1,6 +1,7 @@
use camera::Camera;
use hitable::Hit;
use hitable_list::HitableList;
use kdtree::KDTree;
use material::Lambertian;
use material::Metal;
use moving_sphere::MovingSphere;
@ -15,7 +16,7 @@ pub fn new(opt: &Opt) -> Scene {
let dist_to_focus = (lookfrom - lookat).length();
let aperture = 0.1;
let time_min = 0.;
let time_max = 1.;
let time_max = 0.;
let camera = Camera::new(
lookfrom,
lookat,
@ -27,7 +28,14 @@ pub fn new(opt: &Opt) -> Scene {
time_min,
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(
Vec3::new(0., 0., -1.),
0.5,
@ -36,7 +44,7 @@ pub fn new(opt: &Opt) -> Scene {
Box::new(Sphere::new(
Vec3::new(0., -100.5, -1.),
100.,
Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.8))),
Box::new(Lambertian::new(ground_color)),
)),
Box::new(Sphere::new(
Vec3::new(1., 0., -1.),
@ -51,7 +59,12 @@ pub fn new(opt: &Opt) -> Scene {
1.,
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 {
camera,
world,