Implement motion blur
This commit is contained in:
parent
558851b5f6
commit
3de572d4a9
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
29
src/main.rs
29
src/main.rs
@ -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();
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
src/ray.rs
10
src/ray.rs
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user