diff --git a/src/camera.rs b/src/camera.rs index 86b2f7a..29a49f0 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,3 +1,5 @@ +use rand::Rng; + use crate::{Point3, Ray, Vec3}; pub struct Camera { @@ -11,18 +13,22 @@ pub struct Camera { lens_radius: f64, u: Vec3, v: Vec3, - w: Vec3 + w: Vec3, + time0: f64, + time1: f64 } impl Camera { pub fn get_ray(&self, s: f64, t: f64) -> Ray { let rd = self.lens_radius * Vec3::random_unit_disk(); let offset = self.u * rd.x() + self.v * rd.y(); + let time = rand::thread_rng().gen_range(self.time0..self.time1); Ray::new( self.origin + offset, - self.lower_left_corner + s*self.horizontal + t*self.vertical - self.origin - offset) + self.lower_left_corner + s*self.horizontal + t*self.vertical - self.origin - offset, + time) } - pub fn new( + pub fn still( look_from: Point3, look_at: Point3, up: Vec3, @@ -31,6 +37,29 @@ impl Camera { aperture: f64, focus_dist: f64) -> Self { + Camera::new( + look_from, + look_at, + up, + aspect_ratio, + vfov, + aperture, + focus_dist, + 0.0, + 0.0) + } + + pub fn new( + look_from: Point3, + look_at: Point3, + up: Vec3, + aspect_ratio: f64, + vfov: f64, + aperture: f64, + focus_dist: f64, + time0: f64, + time1: f64) -> Self { + let theta = vfov.to_radians(); let h = (theta / 2.0).tan(); let viewport_height = 2.0 * h; @@ -53,7 +82,9 @@ impl Camera { lens_radius: aperture / 2.0, u, v, - w + w, + time0, + time1 } } } diff --git a/src/hittable.rs b/src/hittable.rs index 51f25b6..5ad460c 100644 --- a/src/hittable.rs +++ b/src/hittable.rs @@ -62,3 +62,68 @@ impl Hittable for Sphere { }) } } + +pub struct MovableSphere { + pub center0: Point3, + pub center1: Point3, + pub radius: f64, + pub material: Material, + pub time0: f64, + pub time1: f64, +} +impl MovableSphere { + pub fn new( + center0: Point3, + center1: Point3, + radius: f64, + material: Material, + time0: f64, + time1: f64) -> Self { + MovableSphere { center0, center1, radius, material, time0, time1 } + } + pub fn center(&self, time: f64) -> Point3 { + self.center0 + ((time - self.time0) / (self.time1 - self.time0))*(self.center1 - self.center0) + } +} +impl Default for MovableSphere { + fn default() -> Self { + MovableSphere { + center0: Point3::default(), + center1: Point3::default(), + radius: 0.0, + material: Material::default(), + time0: 0.0, + time1: 0.0 + } + } +} + +impl Hittable for MovableSphere { + fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option { + let oc = &ray.origin() - &self.center(ray.time()); + let a = ray.direction().length_squared(); + let half_b = oc.dot(&ray.direction()); + let c = oc.length_squared() - &self.radius * &self.radius; + let discriminant = half_b * half_b - a * c; + if discriminant < 0.0 { return None; } + + let sqrtd = discriminant.sqrt(); + let mut root = (-half_b - sqrtd) / a; + if root < t_min || t_max < root { + root = (-half_b + sqrtd) / a; + if root < t_min || t_max < root { return None; } + } + let point = ray.at(root); + let normal = (point - self.center(ray.time())) / self.radius; + let dot = ray.direction().dot(&normal); + let front_face = dot < 0.0; + let normal = if front_face { normal } else { -normal }; + Some(HitRecord { + point, + normal, + t: root, + front_face, + material: &self.material + }) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 2864834..ff88a18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ use crate::hittable::{Hittable, Sphere}; use crate::output::{Output, P3, PNG}; use crate::ray::Ray; use crate::vec3::{Color, Point3, Vec3}; +use hittable::MovableSphere; +use rand::Rng; use rand::distributions::{Distribution, Uniform}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use crate::material::{Dielectric, Lambertian, Material, Metal}; @@ -50,11 +52,24 @@ fn random_scene() -> Vec> { fuzz_range.sample(&mut rng))), _ => Material::Dielectric(Dielectric::new(1.5)), }; - let sphere = Box::new(Sphere { - center, - radius: 0.2, - material - }); + let sphere: Box = match rng.gen_bool(1.0 / 3.0) { + true => { + let center1 = center + Vec3::new(0.0, fuzz_range.sample(&mut rng) / 2.0, 0.0); + Box::new(MovableSphere { + center0: center, + center1, + radius: 0.2, + material, + time0: 0.0, + time1: 1.0 + }) + } + false => Box::new(Sphere { + center, + radius: 0.2, + material + }) + }; world.push(sphere); } } @@ -100,7 +115,9 @@ fn main() { ASPECT_RATIO, 20.0, 0.1, - focus_dist); + focus_dist, + 0.0, + 1.0); // World let world= random_scene(); diff --git a/src/material.rs b/src/material.rs index 5676a16..fb22001 100644 --- a/src/material.rs +++ b/src/material.rs @@ -45,7 +45,7 @@ impl Scatterable for Lambertian { if direction.near_zero() { direction = hit_record.normal; } - let scattered = Ray::new(hit_record.point, direction); + let scattered = Ray::new(hit_record.point, direction, ray.time()); Some((Some(scattered), self.albedo)) } } @@ -70,7 +70,7 @@ impl Scatterable for Metal { let reflected = ray.direction().unit_vector().reflected(&hit_record.normal); let scattered = Ray::new( hit_record.point, - reflected + self.fuzz * Vec3::random_in_unit_sphere()); + reflected + self.fuzz * Vec3::random_in_unit_sphere(), ray.time()); if scattered.direction().dot(&hit_record.normal) > 0.0 { Some((Some(scattered), self.albedo)) } else { @@ -113,11 +113,11 @@ impl Scatterable for Dielectric { if cannot_refract || reflectance > rng.gen::() { let reflected = unit_direction.reflected(&hit_record.normal); - let scattered = Ray::new(hit_record.point, reflected); + let scattered = Ray::new(hit_record.point, reflected, ray.time()); Some((Some(scattered), color)) } else { let direction = unit_direction.refract(&hit_record.normal, refraction_ratio); - let scattered = Ray::new(hit_record.point, direction); + let scattered = Ray::new(hit_record.point, direction, ray.time()); Some((Some(scattered), color)) } } diff --git a/src/ray.rs b/src/ray.rs index 497e772..5a06a99 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -6,23 +6,25 @@ use crate::vec3::Point3; #[derive(Debug)] pub struct Ray { origin: Point3, - direction: Vec3 + direction: Vec3, + time: f64 } impl Default for Ray { fn default() -> Self { - Ray::new(Vec3::default(), Vec3::default()) + Ray::new(Vec3::default(), Vec3::default(), 0.0) } } impl Ray { - pub fn new(origin: Point3, direction: Point3) -> Ray { - Ray { origin, direction } + pub fn new(origin: Point3, direction: Point3, time: f64) -> Ray { + Ray { origin, direction, time } } pub fn at(&self, t: f64) -> Point3 { self.origin + self.direction * t } pub fn direction(&self) -> Vec3 { self.direction } pub fn origin(&self) -> Point3 { self.origin } + pub fn time(&self) -> f64 { self.time } pub fn pixel_color(&self, world: &Vec>, depth: i32) -> Color { if depth <= 0 { return Color::default();