Implement motion blur

master
Max Nuding 2022-07-03 20:29:06 +02:00
parent 558851b5f6
commit 3de572d4a9
Signed by: phlaym
GPG Key ID: 0AAD39863E09DC48
5 changed files with 133 additions and 18 deletions

View File

@ -1,3 +1,5 @@
use rand::Rng;
use crate::{Point3, Ray, Vec3}; use crate::{Point3, Ray, Vec3};
pub struct Camera { pub struct Camera {
@ -11,18 +13,22 @@ pub struct Camera {
lens_radius: f64, lens_radius: f64,
u: Vec3, u: Vec3,
v: Vec3, v: Vec3,
w: Vec3 w: Vec3,
time0: f64,
time1: f64
} }
impl Camera { impl Camera {
pub fn get_ray(&self, s: f64, t: f64) -> Ray { pub fn get_ray(&self, s: f64, t: f64) -> Ray {
let rd = self.lens_radius * Vec3::random_unit_disk(); let rd = self.lens_radius * Vec3::random_unit_disk();
let offset = self.u * rd.x() + self.v * rd.y(); let offset = self.u * rd.x() + self.v * rd.y();
let time = rand::thread_rng().gen_range(self.time0..self.time1);
Ray::new( Ray::new(
self.origin + offset, 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_from: Point3,
look_at: Point3, look_at: Point3,
up: Vec3, up: Vec3,
@ -31,6 +37,29 @@ impl Camera {
aperture: f64, aperture: f64,
focus_dist: f64) -> Self { 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 theta = vfov.to_radians();
let h = (theta / 2.0).tan(); let h = (theta / 2.0).tan();
let viewport_height = 2.0 * h; let viewport_height = 2.0 * h;
@ -53,7 +82,9 @@ impl Camera {
lens_radius: aperture / 2.0, lens_radius: aperture / 2.0,
u, u,
v, v,
w w,
time0,
time1
} }
} }
} }

View File

@ -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<HitRecord> {
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
})
}
}

View File

@ -6,6 +6,8 @@ use crate::hittable::{Hittable, Sphere};
use crate::output::{Output, P3, PNG}; use crate::output::{Output, P3, PNG};
use crate::ray::Ray; use crate::ray::Ray;
use crate::vec3::{Color, Point3, Vec3}; use crate::vec3::{Color, Point3, Vec3};
use hittable::MovableSphere;
use rand::Rng;
use rand::distributions::{Distribution, Uniform}; use rand::distributions::{Distribution, Uniform};
use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::iter::{IntoParallelIterator, ParallelIterator};
use crate::material::{Dielectric, Lambertian, Material, Metal}; use crate::material::{Dielectric, Lambertian, Material, Metal};
@ -50,11 +52,24 @@ fn random_scene() -> Vec<Box<dyn Hittable + Sync>> {
fuzz_range.sample(&mut rng))), fuzz_range.sample(&mut rng))),
_ => Material::Dielectric(Dielectric::new(1.5)), _ => Material::Dielectric(Dielectric::new(1.5)),
}; };
let sphere = Box::new(Sphere { let sphere: Box<dyn Hittable + Sync> = match rng.gen_bool(1.0 / 3.0) {
center, true => {
radius: 0.2, let center1 = center + Vec3::new(0.0, fuzz_range.sample(&mut rng) / 2.0, 0.0);
material 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); world.push(sphere);
} }
} }
@ -100,7 +115,9 @@ fn main() {
ASPECT_RATIO, ASPECT_RATIO,
20.0, 20.0,
0.1, 0.1,
focus_dist); focus_dist,
0.0,
1.0);
// World // World
let world= random_scene(); let world= random_scene();

View File

@ -45,7 +45,7 @@ impl Scatterable for Lambertian {
if direction.near_zero() { if direction.near_zero() {
direction = hit_record.normal; 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)) Some((Some(scattered), self.albedo))
} }
} }
@ -70,7 +70,7 @@ impl Scatterable for Metal {
let reflected = ray.direction().unit_vector().reflected(&hit_record.normal); let reflected = ray.direction().unit_vector().reflected(&hit_record.normal);
let scattered = Ray::new( let scattered = Ray::new(
hit_record.point, 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 { if scattered.direction().dot(&hit_record.normal) > 0.0 {
Some((Some(scattered), self.albedo)) Some((Some(scattered), self.albedo))
} else { } else {
@ -113,11 +113,11 @@ impl Scatterable for Dielectric {
if cannot_refract || reflectance > rng.gen::<f64>() { if cannot_refract || reflectance > rng.gen::<f64>() {
let reflected = unit_direction.reflected(&hit_record.normal); 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)) Some((Some(scattered), color))
} else { } else {
let direction = unit_direction.refract(&hit_record.normal, refraction_ratio); 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)) Some((Some(scattered), color))
} }
} }

View File

@ -6,23 +6,25 @@ use crate::vec3::Point3;
#[derive(Debug)] #[derive(Debug)]
pub struct Ray { pub struct Ray {
origin: Point3, origin: Point3,
direction: Vec3 direction: Vec3,
time: f64
} }
impl Default for Ray { impl Default for Ray {
fn default() -> Self { fn default() -> Self {
Ray::new(Vec3::default(), Vec3::default()) Ray::new(Vec3::default(), Vec3::default(), 0.0)
} }
} }
impl Ray { impl Ray {
pub fn new(origin: Point3, direction: Point3) -> Ray { pub fn new(origin: Point3, direction: Point3, time: f64) -> Ray {
Ray { origin, direction } Ray { origin, direction, time }
} }
pub fn at(&self, t: f64) -> Point3 { pub fn at(&self, t: f64) -> Point3 {
self.origin + self.direction * t self.origin + self.direction * t
} }
pub fn direction(&self) -> Vec3 { self.direction } pub fn direction(&self) -> Vec3 { self.direction }
pub fn origin(&self) -> Point3 { self.origin } pub fn origin(&self) -> Point3 { self.origin }
pub fn time(&self) -> f64 { self.time }
pub fn pixel_color(&self, world: &Vec<Box<dyn Hittable + Sync>>, depth: i32) -> Color { pub fn pixel_color(&self, world: &Vec<Box<dyn Hittable + Sync>>, depth: i32) -> Color {
if depth <= 0 { if depth <= 0 {
return Color::default(); return Color::default();