From 896ae20196f2b6f007337e14827693513bff1c6b Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Fri, 14 Sep 2018 12:06:29 -0700 Subject: [PATCH] Add motion blur and MovingSphere type to exercise it. Also changes Ray implementation to use public struct members instead of accessor functions. --- rtiow/src/bin/tracer.rs | 27 ++++++----- rtiow/src/bin/tracer_blue_sky.rs | 46 ------------------- rtiow/src/bin/tracer_norm_shade.rs | 51 --------------------- rtiow/src/bin/tracer_red_dot.rs | 46 ------------------- rtiow/src/camera.rs | 9 ++++ rtiow/src/lib.rs | 1 + rtiow/src/material.rs | 42 +++++++++-------- rtiow/src/moving_sphere.rs | 72 ++++++++++++++++++++++++++++++ rtiow/src/ray.rs | 21 +++++---- rtiow/src/renderer.rs | 2 +- rtiow/src/sphere.rs | 6 +-- 11 files changed, 135 insertions(+), 188 deletions(-) delete mode 100644 rtiow/src/bin/tracer_blue_sky.rs delete mode 100644 rtiow/src/bin/tracer_norm_shade.rs delete mode 100644 rtiow/src/bin/tracer_red_dot.rs create mode 100644 rtiow/src/moving_sphere.rs diff --git a/rtiow/src/bin/tracer.rs b/rtiow/src/bin/tracer.rs index 607591d..a7f2224 100644 --- a/rtiow/src/bin/tracer.rs +++ b/rtiow/src/bin/tracer.rs @@ -17,13 +17,12 @@ 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; -const BOOK_COVER: bool = true; - fn random_scene() -> Vec> { let mut rng = rand::thread_rng(); let mut objects: Vec> = vec![Box::new(Sphere::new( @@ -98,6 +97,8 @@ fn build_scene_book(opt: &Opt) -> Scene { 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, @@ -106,6 +107,8 @@ fn build_scene_book(opt: &Opt) -> Scene { opt.width as f32 / opt.height as f32, aperture, dist_to_focus, + time_min, + time_max, ); let world = HitableList::new(random_scene()); Scene { @@ -122,6 +125,8 @@ fn build_scene_tutorial(opt: &Opt) -> Scene { 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, @@ -130,6 +135,8 @@ fn build_scene_tutorial(opt: &Opt) -> Scene { opt.width as f32 / opt.height as f32, aperture, dist_to_focus, + time_min, + time_max, ); let world = HitableList::new(vec![ Box::new(Sphere::new( @@ -140,22 +147,20 @@ fn build_scene_tutorial(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.))), + 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(Sphere::new( - Vec3::new(-1., 0., -1.), + Box::new(MovingSphere::new( + Vec3::new(-1., 0., -1.25), + Vec3::new(-1., 0., -0.75), 0.5, - Box::new(Dielectric::new(1.5)), - )), - Box::new(Sphere::new( - Vec3::new(-1., 0., -1.), - -0.45, - Box::new(Dielectric::new(1.5)), + 0., + 1., + Box::new(Lambertian::new(Vec3::new(0.2, 0.8, 0.2))), )), ]); Scene { diff --git a/rtiow/src/bin/tracer_blue_sky.rs b/rtiow/src/bin/tracer_blue_sky.rs deleted file mode 100644 index bac8282..0000000 --- a/rtiow/src/bin/tracer_blue_sky.rs +++ /dev/null @@ -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(()) -} diff --git a/rtiow/src/bin/tracer_norm_shade.rs b/rtiow/src/bin/tracer_norm_shade.rs deleted file mode 100644 index 88a6484..0000000 --- a/rtiow/src/bin/tracer_norm_shade.rs +++ /dev/null @@ -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(()) -} diff --git a/rtiow/src/bin/tracer_red_dot.rs b/rtiow/src/bin/tracer_red_dot.rs deleted file mode 100644 index bac8282..0000000 --- a/rtiow/src/bin/tracer_red_dot.rs +++ /dev/null @@ -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(()) -} diff --git a/rtiow/src/camera.rs b/rtiow/src/camera.rs index 1b961c6..6e8e281 100644 --- a/rtiow/src/camera.rs +++ b/rtiow/src/camera.rs @@ -30,6 +30,8 @@ pub struct Camera { u: Vec3, v: Vec3, lens_radius: f32, + time_min: f32, + time_max: f32, } impl Camera { @@ -42,6 +44,8 @@ impl Camera { aspect: f32, aperture: f32, focus_dist: f32, + time_min: f32, + time_max: f32, ) -> Camera { let theta = vfov * PI / 180.; let half_height = (theta / 2.).tan(); @@ -61,15 +65,20 @@ impl Camera { u: cross(vup, w).unit_vector(), v: cross(w, u), lens_radius: aperture / 2., + time_min, + time_max, } } 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 offset = self.u * rd.x + self.v * rd.y; + let time = self.time_min + rng.gen_range::(0., 1.) * (self.time_max - self.time_min); Ray::new( self.origin + offset, self.lower_left_corner + self.horizontal * u + self.vertical * v - self.origin - offset, + time, ) } } diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index 5bb238b..58db044 100644 --- a/rtiow/src/lib.rs +++ b/rtiow/src/lib.rs @@ -2,6 +2,7 @@ pub mod camera; pub mod hitable; pub mod hitable_list; pub mod material; +pub mod moving_sphere; pub mod ray; pub mod renderer; pub mod sphere; diff --git a/rtiow/src/material.rs b/rtiow/src/material.rs index b40b6af..abb4891 100644 --- a/rtiow/src/material.rs +++ b/rtiow/src/material.rs @@ -43,11 +43,11 @@ impl 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(); ScatterResponse { attenutation: self.albedo, - scattered: Ray::new(rec.p, target - rec.p), + scattered: Ray::new(rec.p, target - rec.p, r_in.time), reflected: true, } } @@ -71,12 +71,16 @@ impl Metal { impl Material for Metal { fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse { - let reflected = reflect(r_in.direction().unit_vector(), rec.normal); - let scattered = Ray::new(rec.p, reflected + self.fuzzy * random_in_unit_sphere()); + let reflected = reflect(r_in.direction.unit_vector(), rec.normal); + let scattered = Ray::new( + rec.p, + reflected + self.fuzzy * random_in_unit_sphere(), + r_in.time, + ); ScatterResponse { scattered, attenutation: self.albedo, - reflected: dot(scattered.direction(), rec.normal) > 0., + reflected: dot(scattered.direction, rec.normal) > 0., } } } @@ -109,31 +113,31 @@ impl Dielectric { impl Material for Dielectric { fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse { - let reflected = reflect(r_in.direction(), rec.normal); - let (outward_normal, ni_over_nt, cosine) = if dot(r_in.direction(), rec.normal) > 0. { + let reflected = reflect(r_in.direction, rec.normal); + let (outward_normal, ni_over_nt, cosine) = if dot(r_in.direction, rec.normal) > 0. { ( -rec.normal, 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 { ( rec.normal, 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 = - if let Some(refracted) = refract(r_in.direction(), outward_normal, ni_over_nt) { - let mut rng = rand::thread_rng(); - if rng.gen_range::(0., 1.) < schlick(cosine, self.ref_idx) { - Ray::new(rec.p, reflected) - } else { - Ray::new(rec.p, refracted) - } + let scattered = if let Some(refracted) = refract(r_in.direction, outward_normal, ni_over_nt) + { + let mut rng = rand::thread_rng(); + if rng.gen_range::(0., 1.) < schlick(cosine, self.ref_idx) { + Ray::new(rec.p, reflected, r_in.time) } else { - Ray::new(rec.p, reflected) - }; + Ray::new(rec.p, refracted, r_in.time) + } + } else { + Ray::new(rec.p, reflected, r_in.time) + }; ScatterResponse { attenutation: Vec3::new(1., 1., 1.), diff --git a/rtiow/src/moving_sphere.rs b/rtiow/src/moving_sphere.rs new file mode 100644 index 0000000..7b5e45d --- /dev/null +++ b/rtiow/src/moving_sphere.rs @@ -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, + time0: f32, + time1: f32, +} + +impl MovingSphere { + pub fn new( + center0: Vec3, + center1: Vec3, + radius: f32, + time0: f32, + time1: f32, + material: Box, + ) -> 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 { + 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 + } +} diff --git a/rtiow/src/ray.rs b/rtiow/src/ray.rs index 5e094e6..7d046c4 100644 --- a/rtiow/src/ray.rs +++ b/rtiow/src/ray.rs @@ -2,21 +2,20 @@ use vec3::Vec3; #[derive(Copy, Clone, Default)] pub struct Ray { - a: Vec3, - b: Vec3, + pub origin: Vec3, + pub direction: Vec3, + pub time: f32, } impl Ray { - pub fn new(a: Vec3, b: Vec3) -> Ray { - Ray { a, b } - } - pub fn origin(self) -> Vec3 { - self.a - } - pub fn direction(self) -> Vec3 { - self.b + pub fn new(origin: Vec3, direction: Vec3, time: f32) -> Ray { + Ray { + origin, + direction, + time, + } } pub fn point_at_parameter(self, t: f32) -> Vec3 { - self.a + self.b * t + self.origin + self.direction * t } } diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index 19ec5f7..bb2fc20 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -34,7 +34,7 @@ fn color(r: Ray, world: &Hit, depth: usize) -> Vec3 { return Default::default(); } // 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.); Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t } diff --git a/rtiow/src/sphere.rs b/rtiow/src/sphere.rs index 42325a1..fdde748 100644 --- a/rtiow/src/sphere.rs +++ b/rtiow/src/sphere.rs @@ -23,9 +23,9 @@ impl Sphere { impl Hit for Sphere { fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { - let oc = r.origin() - self.center; - let a = dot(r.direction(), r.direction()); - let b = dot(oc, r.direction()); + let oc = r.origin - self.center; + 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. {