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::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<Box<Hit>> {
let mut rng = rand::thread_rng();
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 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 {

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,
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::<f32>(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,
)
}
}

View File

@ -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;

View File

@ -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::<f32>(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::<f32>(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.),

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)]
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
}
}

View File

@ -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
}

View File

@ -23,9 +23,9 @@ impl Sphere {
impl Hit for Sphere {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
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. {