Move scene building to its own set of submodules.

This commit is contained in:
Bill Thiede 2018-09-18 18:57:20 -07:00
parent aa26c79f6d
commit 57ccefbcdc
8 changed files with 371 additions and 332 deletions

View File

@ -3,340 +3,16 @@ extern crate log;
extern crate rand;
extern crate rtiow;
extern crate stderrlog;
#[macro_use]
extern crate structopt;
use std::fmt;
use std::path::PathBuf;
use std::str;
use std::time::Instant;
use rand::Rng;
use structopt::StructOpt;
use rtiow::bvh::BVH;
use rtiow::camera::Camera;
use rtiow::cube::Cube;
use rtiow::hitable::Hit;
use rtiow::hitable_list::HitableList;
use rtiow::material::Dielectric;
use rtiow::material::Lambertian;
use rtiow::material::Metal;
use rtiow::moving_sphere::MovingSphere;
use rtiow::renderer::render;
use rtiow::renderer::Scene;
use rtiow::sphere::Sphere;
use rtiow::vec3::Vec3;
fn random_scene() -> 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))),
))];
let mut random = || rng.gen_range::<f32>(0., 1.);
for a in -11..11 {
for b in -11..11 {
let choose_mat = random();
let center = Vec3::new(a as f32 + 0.9 * random(), 0.2, b as f32 + 0.9 * random());
if (center - Vec3::new(4., 0.2, 0.)).length() > 0.9 {
let sphere = if choose_mat < 0.8 {
// diffuse
Box::new(Sphere::new(
center,
0.2,
Box::new(Lambertian::new(Vec3::new(
random() * random(),
random() * random(),
random() * random(),
))),
))
} else if choose_mat < 0.95 {
// metal
Box::new(Sphere::new(
center,
0.2,
Box::new(Metal::new(
Vec3::new(
0.5 * (1. + random()),
0.5 * (1. + random()),
0.5 * (1. + random()),
),
0.5 * random(),
)),
))
} else {
// glass
Box::new(Sphere::new(center, 0.2, Box::new(Dielectric::new(1.5))))
};
objects.push(sphere);
};
}
}
let more: Vec<Box<Hit>> = vec![
Box::new(Sphere::new(
Vec3::new(0., 1., 0.),
1.0,
Box::new(Dielectric::new(1.5)),
)),
Box::new(Sphere::new(
Vec3::new(-4., 1., 0.),
1.0,
Box::new(Lambertian::new(Vec3::new(0.4, 0.2, 0.1))),
)),
Box::new(Sphere::new(
Vec3::new(4., 1., 0.),
1.0,
Box::new(Metal::new(Vec3::new(0.7, 0.6, 0.5), 0.0)),
)),
];
objects.extend(more);
objects
}
fn build_scene_book(bvh: bool, opt: &Opt) -> Scene {
let lookfrom = Vec3::new(13., 2., 3.);
let lookat = Vec3::new(0., 0., 0.);
let dist_to_focus = 10.;
let aperture = 0.1;
let time_min = 0.;
let time_max = 1.;
let camera = Camera::new(
lookfrom,
lookat,
Vec3::new(0., 1., 0.),
20.,
opt.width as f32 / opt.height as f32,
aperture,
dist_to_focus,
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);
} else {
world = Box::new(HitableList::new(random_scene()));
}
Scene {
camera,
world,
subsamples: opt.subsamples,
width: opt.width,
height: opt.height,
}
}
fn build_scene_bvh(opt: &Opt) -> Scene {
let lookfrom = Vec3::new(3., 3., 2.);
let lookat = Vec3::new(0., 0., -1.);
let dist_to_focus = (lookfrom - lookat).length();
let aperture = 0.1;
let time_min = 0.;
let time_max = 1.;
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 b = BVH::new(
vec![
Box::new(Sphere::new(
Vec3::new(0., 0., -1.),
0.5,
Box::new(Lambertian::new(Vec3::new(0.1, 0.2, 0.5))),
)),
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(Sphere::new(
Vec3::new(1., 0., -1.),
0.5,
Box::new(Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2)),
)),
Box::new(MovingSphere::new(
Vec3::new(-1., 0., -1.25),
Vec3::new(-1., 0., -0.75),
0.5,
time_min,
time_max,
Box::new(Lambertian::new(Vec3::new(0.2, 0.8, 0.2))),
)),
],
time_min,
time_max,
);
trace!(target: "bvh", "World {}", b);
let world: Box<Hit> = Box::new(b);
Scene {
camera,
world,
subsamples: opt.subsamples,
width: opt.width,
height: opt.height,
}
}
fn build_scene_tutorial(opt: &Opt) -> Scene {
let lookfrom = Vec3::new(3., 3., 2.);
let lookat = Vec3::new(0., 0., -1.);
let dist_to_focus = (lookfrom - lookat).length();
let aperture = 0.1;
let time_min = 0.;
let time_max = 1.;
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 world: Box<Hit> = Box::new(HitableList::new(vec![
Box::new(Sphere::new(
Vec3::new(0., 0., -1.),
0.5,
Box::new(Lambertian::new(Vec3::new(0.1, 0.2, 0.5))),
)),
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(Sphere::new(
Vec3::new(1., 0., -1.),
0.5,
Box::new(Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.2)),
)),
Box::new(MovingSphere::new(
Vec3::new(-1., 0., -1.25),
Vec3::new(-1., 0., -0.75),
0.5,
0.,
1.,
Box::new(Lambertian::new(Vec3::new(0.2, 0.8, 0.2))),
)),
]));
Scene {
camera,
world,
subsamples: opt.subsamples,
width: opt.width,
height: opt.height,
}
}
fn build_scene_cube(opt: &Opt) -> Scene {
let lookfrom = Vec3::new(3., 3., 2.);
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 = 1.;
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 world: Box<Hit> = Box::new(HitableList::new(vec![
Box::new(Sphere::new(
Vec3::new(0., 0., -1.),
0.5,
Box::new(Lambertian::new(Vec3::new(0.1, 0.2, 0.5))),
)),
Box::new(Cube::new(
Vec3::new(0., 0., 0.),
0.5,
Box::new(Lambertian::new(Vec3::new(0.5, 0.2, 0.1))),
)),
Box::new(Sphere::new(
Vec3::new(0., -100.5, -1.),
100.,
Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.8))),
)),
]));
Scene {
camera,
world,
subsamples: opt.subsamples,
width: opt.width,
height: opt.height,
}
}
#[derive(Debug)]
pub enum Model {
Book,
BookBVH,
Tutorial,
Cube,
BVH,
}
#[derive(Debug)]
pub struct ModelParseError(String);
impl fmt::Display for ModelParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unknown model enum type '{}'", self.0)
}
}
impl str::FromStr for Model {
type Err = ModelParseError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"book" => Ok(Model::Book),
"book_bvh" => Ok(Model::BookBVH),
"tutorial" => Ok(Model::Tutorial),
"cube" => Ok(Model::Cube),
"bvh" => Ok(Model::BVH),
_ => Err(ModelParseError(s.to_owned())),
}
}
}
#[derive(Debug, StructOpt)]
#[structopt(name = "tracer", about = "An experimental ray tracer.")]
pub struct Opt {
/// Image width
#[structopt(short = "w", long = "width", default_value = "1280")]
pub width: usize,
/// Image height
#[structopt(short = "h", long = "height", default_value = "720")]
pub height: usize,
/// Sub-samples per pixel
#[structopt(short = "s", long = "subsample", default_value = "10")]
pub subsamples: usize,
#[structopt(long = "model")]
pub model: Model,
/// Output directory
#[structopt(parse(from_os_str))]
pub output: PathBuf,
}
use rtiow::renderer::Model;
use rtiow::renderer::Opt;
use rtiow::scenes;
fn main() -> Result<(), std::io::Error> {
stderrlog::new()
@ -347,11 +23,11 @@ fn main() -> Result<(), std::io::Error> {
let start = Instant::now();
let opt = Opt::from_args();
let scene = match opt.model {
Model::Book => build_scene_book(false, &opt),
Model::BookBVH => build_scene_book(true, &opt),
Model::Cube => build_scene_cube(&opt),
Model::Tutorial => build_scene_tutorial(&opt),
Model::BVH => build_scene_bvh(&opt),
Model::Book => scenes::book::new(false, &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 res = render(scene, &opt.output);
let runtime = start.elapsed();

View File

@ -8,6 +8,7 @@ pub mod material;
pub mod moving_sphere;
pub mod ray;
pub mod renderer;
pub mod scenes;
pub mod sphere;
pub mod vec3;
@ -18,3 +19,5 @@ extern crate log;
extern crate num_cpus;
extern crate rand;
extern crate rayon;
#[macro_use]
extern crate structopt;

View File

@ -1,5 +1,8 @@
use std;
use std::fmt;
use std::path::Path;
use std::path::PathBuf;
use std::str;
use std::sync;
use std::thread;
@ -15,6 +18,58 @@ use hitable::Hit;
use ray::Ray;
use vec3::Vec3;
#[derive(Debug)]
pub enum Model {
Book,
BookBVH,
Tutorial,
Cube,
BVH,
}
#[derive(Debug)]
pub struct ModelParseError(String);
impl fmt::Display for ModelParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unknown model enum type '{}'", self.0)
}
}
impl str::FromStr for Model {
type Err = ModelParseError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"book" => Ok(Model::Book),
"book_bvh" => Ok(Model::BookBVH),
"tutorial" => Ok(Model::Tutorial),
"cube" => Ok(Model::Cube),
"bvh" => Ok(Model::BVH),
_ => Err(ModelParseError(s.to_owned())),
}
}
}
#[derive(Debug, StructOpt)]
#[structopt(name = "tracer", about = "An experimental ray tracer.")]
pub struct Opt {
/// Image width
#[structopt(short = "w", long = "width", default_value = "1280")]
pub width: usize,
/// Image height
#[structopt(short = "h", long = "height", default_value = "720")]
pub height: usize,
/// Sub-samples per pixel
#[structopt(short = "s", long = "subsample", default_value = "10")]
pub subsamples: usize,
#[structopt(long = "model")]
pub model: Model,
/// Output directory
#[structopt(parse(from_os_str))]
pub output: PathBuf,
}
pub struct Scene {
pub world: Box<Hit>,
pub camera: Camera,

118
rtiow/src/scenes/book.rs Normal file
View File

@ -0,0 +1,118 @@
use rand;
use rand::Rng;
use bvh::BVH;
use camera::Camera;
use hitable::Hit;
use hitable_list::HitableList;
use material::Dielectric;
use material::Lambertian;
use material::Metal;
use renderer::Opt;
use renderer::Scene;
use sphere::Sphere;
use vec3::Vec3;
pub fn new(bvh: bool, opt: &Opt) -> Scene {
let lookfrom = Vec3::new(13., 2., 3.);
let lookat = Vec3::new(0., 0., 0.);
let dist_to_focus = 10.;
let aperture = 0.1;
let time_min = 0.;
let time_max = 1.;
let camera = Camera::new(
lookfrom,
lookat,
Vec3::new(0., 1., 0.),
20.,
opt.width as f32 / opt.height as f32,
aperture,
dist_to_focus,
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);
} else {
world = Box::new(HitableList::new(random_scene()));
}
Scene {
camera,
world,
subsamples: opt.subsamples,
width: opt.width,
height: opt.height,
}
}
fn random_scene() -> 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))),
))];
let mut random = || rng.gen_range::<f32>(0., 1.);
for a in -11..11 {
for b in -11..11 {
let choose_mat = random();
let center = Vec3::new(a as f32 + 0.9 * random(), 0.2, b as f32 + 0.9 * random());
if (center - Vec3::new(4., 0.2, 0.)).length() > 0.9 {
let sphere = if choose_mat < 0.8 {
// diffuse
Box::new(Sphere::new(
center,
0.2,
Box::new(Lambertian::new(Vec3::new(
random() * random(),
random() * random(),
random() * random(),
))),
))
} else if choose_mat < 0.95 {
// metal
Box::new(Sphere::new(
center,
0.2,
Box::new(Metal::new(
Vec3::new(
0.5 * (1. + random()),
0.5 * (1. + random()),
0.5 * (1. + random()),
),
0.5 * random(),
)),
))
} else {
// glass
Box::new(Sphere::new(center, 0.2, Box::new(Dielectric::new(1.5))))
};
objects.push(sphere);
};
}
}
let more: Vec<Box<Hit>> = vec![
Box::new(Sphere::new(
Vec3::new(0., 1., 0.),
1.0,
Box::new(Dielectric::new(1.5)),
)),
Box::new(Sphere::new(
Vec3::new(-4., 1., 0.),
1.0,
Box::new(Lambertian::new(Vec3::new(0.4, 0.2, 0.1))),
)),
Box::new(Sphere::new(
Vec3::new(4., 1., 0.),
1.0,
Box::new(Metal::new(Vec3::new(0.7, 0.6, 0.5), 0.0)),
)),
];
objects.extend(more);
objects
}

68
rtiow/src/scenes/bvh.rs Normal file
View File

@ -0,0 +1,68 @@
use bvh::BVH;
use camera::Camera;
use hitable::Hit;
use material::Lambertian;
use material::Metal;
use moving_sphere::MovingSphere;
use renderer::Opt;
use renderer::Scene;
use sphere::Sphere;
use vec3::Vec3;
pub fn new(opt: &Opt) -> Scene {
let lookfrom = Vec3::new(3., 3., 2.);
let lookat = Vec3::new(0., 0., -1.);
let dist_to_focus = (lookfrom - lookat).length();
let aperture = 0.1;
let time_min = 0.;
let time_max = 1.;
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 b = BVH::new(
vec![
Box::new(Sphere::new(
Vec3::new(0., 0., -1.),
0.5,
Box::new(Lambertian::new(Vec3::new(0.1, 0.2, 0.5))),
)),
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(Sphere::new(
Vec3::new(1., 0., -1.),
0.5,
Box::new(Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2)),
)),
Box::new(MovingSphere::new(
Vec3::new(-1., 0., -1.25),
Vec3::new(-1., 0., -0.75),
0.5,
time_min,
time_max,
Box::new(Lambertian::new(Vec3::new(0.2, 0.8, 0.2))),
)),
],
time_min,
time_max,
);
trace!(target: "bvh", "World {}", b);
let world: Box<Hit> = Box::new(b);
Scene {
camera,
world,
subsamples: opt.subsamples,
width: opt.width,
height: opt.height,
}
}

53
rtiow/src/scenes/cube.rs Normal file
View File

@ -0,0 +1,53 @@
use camera::Camera;
use cube::Cube;
use hitable::Hit;
use hitable_list::HitableList;
use material::Lambertian;
use renderer::Opt;
use renderer::Scene;
use sphere::Sphere;
use vec3::Vec3;
pub fn new(opt: &Opt) -> Scene {
let lookfrom = Vec3::new(3., 3., 2.);
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 = 1.;
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 world: Box<Hit> = Box::new(HitableList::new(vec![
Box::new(Sphere::new(
Vec3::new(0., 0., -1.),
0.5,
Box::new(Lambertian::new(Vec3::new(0.1, 0.2, 0.5))),
)),
Box::new(Cube::new(
Vec3::new(0., 0., 0.),
0.5,
Box::new(Lambertian::new(Vec3::new(0.5, 0.2, 0.1))),
)),
Box::new(Sphere::new(
Vec3::new(0., -100.5, -1.),
100.,
Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.8))),
)),
]));
Scene {
camera,
world,
subsamples: opt.subsamples,
width: opt.width,
height: opt.height,
}
}

4
rtiow/src/scenes/mod.rs Normal file
View File

@ -0,0 +1,4 @@
pub mod book;
pub mod bvh;
pub mod cube;
pub mod tutorial;

View File

@ -0,0 +1,62 @@
use camera::Camera;
use hitable::Hit;
use hitable_list::HitableList;
use material::Lambertian;
use material::Metal;
use moving_sphere::MovingSphere;
use renderer::Opt;
use renderer::Scene;
use sphere::Sphere;
use vec3::Vec3;
pub fn new(opt: &Opt) -> Scene {
let lookfrom = Vec3::new(3., 3., 2.);
let lookat = Vec3::new(0., 0., -1.);
let dist_to_focus = (lookfrom - lookat).length();
let aperture = 0.1;
let time_min = 0.;
let time_max = 1.;
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 world: Box<Hit> = Box::new(HitableList::new(vec![
Box::new(Sphere::new(
Vec3::new(0., 0., -1.),
0.5,
Box::new(Lambertian::new(Vec3::new(0.1, 0.2, 0.5))),
)),
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(Sphere::new(
Vec3::new(1., 0., -1.),
0.5,
Box::new(Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.2)),
)),
Box::new(MovingSphere::new(
Vec3::new(-1., 0., -1.25),
Vec3::new(-1., 0., -0.75),
0.5,
0.,
1.,
Box::new(Lambertian::new(Vec3::new(0.2, 0.8, 0.2))),
)),
]));
Scene {
camera,
world,
subsamples: opt.subsamples,
width: opt.width,
height: opt.height,
}
}