Add motion blur and MovingSphere type to exercise it.

Also changes Ray implementation to use public struct members instead of
accessor functions.
This commit is contained in:
Bill Thiede 2018-09-14 12:06:29 -07:00
parent 919fa5f8d5
commit 896ae20196
11 changed files with 135 additions and 188 deletions

View File

@ -17,13 +17,12 @@ use rtiow::hitable_list::HitableList;
use rtiow::material::Dielectric; use rtiow::material::Dielectric;
use rtiow::material::Lambertian; use rtiow::material::Lambertian;
use rtiow::material::Metal; use rtiow::material::Metal;
use rtiow::moving_sphere::MovingSphere;
use rtiow::renderer::render; use rtiow::renderer::render;
use rtiow::renderer::Scene; use rtiow::renderer::Scene;
use rtiow::sphere::Sphere; use rtiow::sphere::Sphere;
use rtiow::vec3::Vec3; use rtiow::vec3::Vec3;
const BOOK_COVER: bool = true;
fn random_scene() -> Vec<Box<Hit>> { fn random_scene() -> 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(
@ -98,6 +97,8 @@ fn build_scene_book(opt: &Opt) -> Scene {
let lookat = Vec3::new(0., 0., 0.); let lookat = Vec3::new(0., 0., 0.);
let dist_to_focus = 10.; let dist_to_focus = 10.;
let aperture = 0.1; let aperture = 0.1;
let time_min = 0.;
let time_max = 1.;
let camera = Camera::new( let camera = Camera::new(
lookfrom, lookfrom,
lookat, lookat,
@ -106,6 +107,8 @@ fn build_scene_book(opt: &Opt) -> Scene {
opt.width as f32 / opt.height as f32, opt.width as f32 / opt.height as f32,
aperture, aperture,
dist_to_focus, dist_to_focus,
time_min,
time_max,
); );
let world = HitableList::new(random_scene()); let world = HitableList::new(random_scene());
Scene { Scene {
@ -122,6 +125,8 @@ fn build_scene_tutorial(opt: &Opt) -> Scene {
let lookat = Vec3::new(0., 0., -1.); let lookat = Vec3::new(0., 0., -1.);
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_max = 1.;
let camera = Camera::new( let camera = Camera::new(
lookfrom, lookfrom,
lookat, lookat,
@ -130,6 +135,8 @@ fn build_scene_tutorial(opt: &Opt) -> Scene {
opt.width as f32 / opt.height as f32, opt.width as f32 / opt.height as f32,
aperture, aperture,
dist_to_focus, dist_to_focus,
time_min,
time_max,
); );
let world = HitableList::new(vec![ let world = HitableList::new(vec![
Box::new(Sphere::new( Box::new(Sphere::new(
@ -140,22 +147,20 @@ fn build_scene_tutorial(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.))), Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.8))),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(1., 0., -1.), Vec3::new(1., 0., -1.),
0.5, 0.5,
Box::new(Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.2)), Box::new(Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.2)),
)), )),
Box::new(Sphere::new( Box::new(MovingSphere::new(
Vec3::new(-1., 0., -1.), Vec3::new(-1., 0., -1.25),
Vec3::new(-1., 0., -0.75),
0.5, 0.5,
Box::new(Dielectric::new(1.5)), 0.,
)), 1.,
Box::new(Sphere::new( Box::new(Lambertian::new(Vec3::new(0.2, 0.8, 0.2))),
Vec3::new(-1., 0., -1.),
-0.45,
Box::new(Dielectric::new(1.5)),
)), )),
]); ]);
Scene { Scene {

View File

@ -1,46 +0,0 @@
extern crate rtiow;
use rtiow::ray::Ray;
use rtiow::vec3::dot;
use rtiow::vec3::Vec3;
fn hit_sphere(center: Vec3, radius: f32, r: Ray) -> bool {
let oc = r.origin() - center;
let a = dot(r.direction(), r.direction());
let b = 2. * dot(oc, r.direction());
let c = dot(oc, oc) - radius * radius;
let discriminant = b * b - 4. * a * c;
discriminant > 0.
}
fn color(r: Ray) -> Vec3 {
if hit_sphere(Vec3::new(0., 0., -1.), 0.5, r) {
return Vec3::new(1., 0., 0.);
}
let unit_direction = r.direction().unit_vector();
let t = 0.5 * (unit_direction.y + 1.);
Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t
}
fn main() -> Result<(), std::io::Error> {
let nx = 200;
let ny = 100;
println!("P3\n{} {}\n255", nx, ny);
let lower_left_corner = Vec3::new(-2., -1., -1.);
let horizontal = Vec3::new(4., 0., 0.);
let vertical = Vec3::new(0., 2., 0.);
let origin: Vec3 = Default::default();
for j in (0..ny).rev() {
for i in 0..nx {
let u = i as f32 / nx as f32;
let v = j as f32 / ny as f32;
let r = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let col = color(r);
let ir = (255.99 * col[0]) as u32;
let ig = (255.99 * col[1]) as u32;
let ib = (255.99 * col[2]) as u32;
println!("{} {} {}", ir, ig, ib);
}
}
Ok(())
}

View File

@ -1,51 +0,0 @@
extern crate rtiow;
use rtiow::ray::Ray;
use rtiow::vec3::dot;
use rtiow::vec3::Vec3;
fn hit_sphere(center: Vec3, radius: f32, r: Ray) -> f32 {
let oc = r.origin() - center;
let a = dot(r.direction(), r.direction());
let b = 2. * dot(oc, r.direction());
let c = dot(oc, oc) - radius * radius;
let discriminant = b * b - 4. * a * c;
if discriminant < 0. {
return -1.;
}
(-b - discriminant.sqrt()) / (2. * a)
}
fn color(r: Ray) -> Vec3 {
let t = hit_sphere(Vec3::new(0., 0., -1.), 0.5, r);
if t > 0. {
let n = (r.point_at_parameter(t) - Vec3::new(0., 0., -1.)).unit_vector();
return (n + 1.) * 0.5;
}
let unit_direction = r.direction().unit_vector();
let t = 0.5 * (unit_direction.y + 1.);
Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t
}
fn main() -> Result<(), std::io::Error> {
let nx = 200;
let ny = 100;
println!("P3\n{} {}\n255", nx, ny);
let lower_left_corner = Vec3::new(-2., -1., -1.);
let horizontal = Vec3::new(4., 0., 0.);
let vertical = Vec3::new(0., 2., 0.);
let origin: Vec3 = Default::default();
for j in (0..ny).rev() {
for i in 0..nx {
let u = i as f32 / nx as f32;
let v = j as f32 / ny as f32;
let r = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let col = color(r);
let ir = (255.99 * col[0]) as u32;
let ig = (255.99 * col[1]) as u32;
let ib = (255.99 * col[2]) as u32;
println!("{} {} {}", ir, ig, ib);
}
}
Ok(())
}

View File

@ -1,46 +0,0 @@
extern crate rtiow;
use rtiow::ray::Ray;
use rtiow::vec3::dot;
use rtiow::vec3::Vec3;
fn hit_sphere(center: Vec3, radius: f32, r: Ray) -> bool {
let oc = r.origin() - center;
let a = dot(r.direction(), r.direction());
let b = 2. * dot(oc, r.direction());
let c = dot(oc, oc) - radius * radius;
let discriminant = b * b - 4. * a * c;
discriminant > 0.
}
fn color(r: Ray) -> Vec3 {
if hit_sphere(Vec3::new(0., 0., -1.), 0.5, r) {
return Vec3::new(1., 0., 0.);
}
let unit_direction = r.direction().unit_vector();
let t = 0.5 * (unit_direction.y + 1.);
Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t
}
fn main() -> Result<(), std::io::Error> {
let nx = 200;
let ny = 100;
println!("P3\n{} {}\n255", nx, ny);
let lower_left_corner = Vec3::new(-2., -1., -1.);
let horizontal = Vec3::new(4., 0., 0.);
let vertical = Vec3::new(0., 2., 0.);
let origin: Vec3 = Default::default();
for j in (0..ny).rev() {
for i in 0..nx {
let u = i as f32 / nx as f32;
let v = j as f32 / ny as f32;
let r = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let col = color(r);
let ir = (255.99 * col[0]) as u32;
let ig = (255.99 * col[1]) as u32;
let ib = (255.99 * col[2]) as u32;
println!("{} {} {}", ir, ig, ib);
}
}
Ok(())
}

View File

@ -30,6 +30,8 @@ pub struct Camera {
u: Vec3, u: Vec3,
v: Vec3, v: Vec3,
lens_radius: f32, lens_radius: f32,
time_min: f32,
time_max: f32,
} }
impl Camera { impl Camera {
@ -42,6 +44,8 @@ impl Camera {
aspect: f32, aspect: f32,
aperture: f32, aperture: f32,
focus_dist: f32, focus_dist: f32,
time_min: f32,
time_max: f32,
) -> Camera { ) -> Camera {
let theta = vfov * PI / 180.; let theta = vfov * PI / 180.;
let half_height = (theta / 2.).tan(); let half_height = (theta / 2.).tan();
@ -61,15 +65,20 @@ impl Camera {
u: cross(vup, w).unit_vector(), u: cross(vup, w).unit_vector(),
v: cross(w, u), v: cross(w, u),
lens_radius: aperture / 2., lens_radius: aperture / 2.,
time_min,
time_max,
} }
} }
pub fn get_ray(&self, u: f32, v: f32) -> Ray { pub fn get_ray(&self, u: f32, v: f32) -> Ray {
let mut rng = rand::thread_rng();
let rd = self.lens_radius * random_in_unit_disk(); let rd = self.lens_radius * random_in_unit_disk();
let offset = self.u * rd.x + self.v * rd.y; let offset = self.u * rd.x + self.v * rd.y;
let time = self.time_min + rng.gen_range::<f32>(0., 1.) * (self.time_max - self.time_min);
Ray::new( Ray::new(
self.origin + offset, self.origin + offset,
self.lower_left_corner + self.horizontal * u + self.vertical * v - self.origin - offset, self.lower_left_corner + self.horizontal * u + self.vertical * v - self.origin - offset,
time,
) )
} }
} }

View File

@ -2,6 +2,7 @@ pub mod camera;
pub mod hitable; pub mod hitable;
pub mod hitable_list; pub mod hitable_list;
pub mod material; pub mod material;
pub mod moving_sphere;
pub mod ray; pub mod ray;
pub mod renderer; pub mod renderer;
pub mod sphere; pub mod sphere;

View File

@ -43,11 +43,11 @@ impl Lambertian {
} }
impl Material for Lambertian { impl Material for Lambertian {
fn scatter(&self, _r_in: &Ray, rec: &HitRecord) -> ScatterResponse { fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse {
let target = rec.p + rec.normal + random_in_unit_sphere(); let target = rec.p + rec.normal + random_in_unit_sphere();
ScatterResponse { ScatterResponse {
attenutation: self.albedo, attenutation: self.albedo,
scattered: Ray::new(rec.p, target - rec.p), scattered: Ray::new(rec.p, target - rec.p, r_in.time),
reflected: true, reflected: true,
} }
} }
@ -71,12 +71,16 @@ impl Metal {
impl Material for Metal { impl Material for Metal {
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse { fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse {
let reflected = reflect(r_in.direction().unit_vector(), rec.normal); let reflected = reflect(r_in.direction.unit_vector(), rec.normal);
let scattered = Ray::new(rec.p, reflected + self.fuzzy * random_in_unit_sphere()); let scattered = Ray::new(
rec.p,
reflected + self.fuzzy * random_in_unit_sphere(),
r_in.time,
);
ScatterResponse { ScatterResponse {
scattered, scattered,
attenutation: self.albedo, attenutation: self.albedo,
reflected: dot(scattered.direction(), rec.normal) > 0., reflected: dot(scattered.direction, rec.normal) > 0.,
} }
} }
} }
@ -109,30 +113,30 @@ impl Dielectric {
impl Material for Dielectric { impl Material for Dielectric {
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse { fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse {
let reflected = reflect(r_in.direction(), rec.normal); let reflected = reflect(r_in.direction, rec.normal);
let (outward_normal, ni_over_nt, cosine) = if dot(r_in.direction(), rec.normal) > 0. { let (outward_normal, ni_over_nt, cosine) = if dot(r_in.direction, rec.normal) > 0. {
( (
-rec.normal, -rec.normal,
self.ref_idx, self.ref_idx,
self.ref_idx * dot(r_in.direction(), rec.normal) / r_in.direction().length(), self.ref_idx * dot(r_in.direction, rec.normal) / r_in.direction.length(),
) )
} else { } else {
( (
rec.normal, rec.normal,
1. / self.ref_idx, 1. / self.ref_idx,
-dot(r_in.direction(), rec.normal) / r_in.direction().length(), -dot(r_in.direction, rec.normal) / r_in.direction.length(),
) )
}; };
let scattered = let scattered = if let Some(refracted) = refract(r_in.direction, outward_normal, ni_over_nt)
if let Some(refracted) = refract(r_in.direction(), outward_normal, ni_over_nt) { {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
if rng.gen_range::<f32>(0., 1.) < schlick(cosine, self.ref_idx) { if rng.gen_range::<f32>(0., 1.) < schlick(cosine, self.ref_idx) {
Ray::new(rec.p, reflected) Ray::new(rec.p, reflected, r_in.time)
} else { } else {
Ray::new(rec.p, refracted) Ray::new(rec.p, refracted, r_in.time)
} }
} else { } else {
Ray::new(rec.p, reflected) Ray::new(rec.p, reflected, r_in.time)
}; };
ScatterResponse { ScatterResponse {

View File

@ -0,0 +1,72 @@
use hitable::Hit;
use hitable::HitRecord;
use material::Material;
use ray::Ray;
use vec3::dot;
use vec3::Vec3;
pub struct MovingSphere {
center0: Vec3,
center1: Vec3,
radius: f32,
material: Box<Material>,
time0: f32,
time1: f32,
}
impl MovingSphere {
pub fn new(
center0: Vec3,
center1: Vec3,
radius: f32,
time0: f32,
time1: f32,
material: Box<Material>,
) -> MovingSphere {
MovingSphere {
center0,
center1,
radius,
material,
time0,
time1,
}
}
fn center(&self, time: f32) -> Vec3 {
self.center0
+ ((time - self.time0) / (self.time1 - self.time0)) * (self.center1 - self.center0)
}
}
impl Hit for MovingSphere {
fn hit(&self, r: Ray, t0: f32, t1: f32) -> Option<HitRecord> {
let oc = r.origin - self.center(r.time);
let a = dot(r.direction, r.direction);
let b = dot(oc, r.direction);
let c = dot(oc, oc) - self.radius * self.radius;
let discriminant = b * b - a * c;
if discriminant > 0. {
let temp = (-b - (b * b - a * c).sqrt()) / a;
if temp < t1 && temp > t0 {
let point = r.point_at_parameter(temp);
return Some(HitRecord {
t: temp,
p: point,
normal: (point - self.center(r.time)) / self.radius,
material: &*self.material,
});
}
let temp = (-b + (b * b - a * c).sqrt()) / a;
if temp < t1 && temp > t0 {
let point = r.point_at_parameter(temp);
return Some(HitRecord {
t: temp,
p: point,
normal: (point - self.center(r.time)) / self.radius,
material: &*self.material,
});
}
}
None
}
}

View File

@ -2,21 +2,20 @@ use vec3::Vec3;
#[derive(Copy, Clone, Default)] #[derive(Copy, Clone, Default)]
pub struct Ray { pub struct Ray {
a: Vec3, pub origin: Vec3,
b: Vec3, pub direction: Vec3,
pub time: f32,
} }
impl Ray { impl Ray {
pub fn new(a: Vec3, b: Vec3) -> Ray { pub fn new(origin: Vec3, direction: Vec3, time: f32) -> Ray {
Ray { a, b } Ray {
origin,
direction,
time,
} }
pub fn origin(self) -> Vec3 {
self.a
}
pub fn direction(self) -> Vec3 {
self.b
} }
pub fn point_at_parameter(self, t: f32) -> Vec3 { pub fn point_at_parameter(self, t: f32) -> Vec3 {
self.a + self.b * t self.origin + self.direction * t
} }
} }

View File

@ -34,7 +34,7 @@ fn color(r: Ray, world: &Hit, depth: usize) -> Vec3 {
return Default::default(); return Default::default();
} }
// No hit, choose color from background. // No hit, choose color from background.
let unit_direction = r.direction().unit_vector(); let unit_direction = r.direction.unit_vector();
let t = 0.5 * (unit_direction.y + 1.); let t = 0.5 * (unit_direction.y + 1.);
Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t
} }

View File

@ -23,9 +23,9 @@ impl Sphere {
impl Hit for Sphere { impl Hit for Sphere {
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> {
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);
let b = dot(oc, r.direction()); let b = dot(oc, r.direction);
let c = dot(oc, oc) - self.radius * self.radius; let c = dot(oc, oc) - self.radius * self.radius;
let discriminant = b * b - a * c; let discriminant = b * b - a * c;
if discriminant > 0. { if discriminant > 0. {