diff --git a/src/camera.rs b/src/camera.rs index 00301a3..9000e6e 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -28,9 +28,9 @@ impl Camera { pub fn new() -> Camera { let aspect_ratio = 16.0 / 9.0; let image_width = 1600; - let samples_per_pixel = 300; - let max_depth = 30; - let vfov: f64 = 50.0; + let samples_per_pixel = 600; + let max_depth = 200; + let vfov: f64 = 30.0; let look_from = Point3::new(-2.0, 2.0, 1.0); let look_at = Point3::new(0.0, 0.0, -1.0); let vup = Vector3::new(0.0, 1.0, 0.0); diff --git a/src/hittable.rs b/src/hittable.rs index 7bc5d5c..2364219 100644 --- a/src/hittable.rs +++ b/src/hittable.rs @@ -33,6 +33,3 @@ impl HitRecord { } } -pub fn length_squared(v: &Vector3) -> f64 { - v.x * v.x + v.y * v.y + v.z * v.z -} diff --git a/src/main.rs b/src/main.rs index 4154e64..e00e66f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,10 +24,15 @@ impl HittableList { impl hittable::Hittable for HittableList { fn hit(&self, ray: &ray::Ray, ray_t: Range) -> Option { - self.objects - .iter() - .flat_map(|obj| obj.hit(ray, ray_t.clone())) - .next() + let (_closest, hit_record) = self.objects.iter().fold((ray_t.end, None), |acc, item| { + if let Some(temp_rec) = item.hit(ray, ray_t.start..acc.0) { + (temp_rec.t, Some(temp_rec)) + } else { + acc + } + }); + + hit_record } } @@ -44,6 +49,11 @@ fn main() { let material_left = Arc::new(material::Dielectric::new(1.5)); let material_right = Arc::new(material::Metal::new(Color::new(0.8, 0.6, 0.2), 0.0)); + world.add(Arc::new(sphere::Sphere::new( + Point3::new(0.0, -100.5, -1.0), + 100.0, + material_ground, + ))); world.add(Arc::new(sphere::Sphere::new( Point3::new(0.0, 0.0, -1.0), 0.5, @@ -64,11 +74,6 @@ fn main() { 0.5, material_right, ))); - world.add(Arc::new(sphere::Sphere::new( - Point3::new(0.0, -100.5, -1.0), - 100.0, - material_ground, - ))); // Render camera.render("target/image.ppm", &world); diff --git a/src/material.rs b/src/material.rs index 78925d0..7fc033e 100644 --- a/src/material.rs +++ b/src/material.rs @@ -4,21 +4,22 @@ use rand::Rng; use crate::types::*; -fn reflect(v: &Vector3, n: &Vector3) -> Vector3 { - *v - 2.0 * v.dot(n) * *n +fn reflect(v: Vector3, n: Vector3) -> Vector3 { + v - 2. * v.dot(&n) * n } -fn refract(uv: &Vector3, n: &Vector3, etai_over_etat: f64) -> Vector3 { - let cos_theta = (-*uv).dot(n).min(1.0); +fn refract(uv: Vector3, n: Vector3, etai_over_etat: f64) -> Vector3 { + let cos_theta = (uv * -1.0).dot(&n).min(1.0); let r_out_perp = etai_over_etat * (uv + cos_theta * n); - let r_out_parallel = -((1.0 - r_out_perp.norm_squared()).abs()).sqrt() * n; + let r_out_parallel = ((1.0 - r_out_perp.magnitude_squared()).abs().sqrt() * -1.0) * n; r_out_perp + r_out_parallel } fn reflectance(cosine: f64, ref_idx: f64) -> f64 { - // Use Schlick's approximation for reflectance - let r0 = ((1.0 - ref_idx) / (1.0 + ref_idx)).powi(2); - r0 + (1.0 - r0) * (1.0 - cosine).powi(5) + // Use Schlick's approximation for reflectance. + let mut r0 = (1. - ref_idx) / (1. + ref_idx); + r0 = r0 * r0; + r0 + (1. - r0) * (1. - cosine).powf(5.) } pub trait Material { @@ -129,27 +130,29 @@ impl Material for Dielectric { ray_in: &ray::Ray, hit_record: &hittable::HitRecord, ) -> Option<(Color, ray::Ray)> { + let mut rng = rand::thread_rng(); + let attenuation = Color::new(1.0, 1.0, 1.0); - let refraction_ratio = if hit_record.front_face { - 1.0 / self.ir + let refraction_ratio: f64 = if hit_record.front_face { + self.ir.recip() } else { self.ir }; let unit_direction = ray_in.dir.normalize(); - let cos_theta = (-unit_direction).dot(&hit_record.normal).min(1.0); + + let cos_theta = (unit_direction.dot(&hit_record.normal) * -1.0).min(1.0); let sin_theta = (1.0 - cos_theta * cos_theta).sqrt(); let cannot_refract = refraction_ratio * sin_theta > 1.0; - let is_reflectance = reflectance(cos_theta, refraction_ratio) > rand::thread_rng().gen(); - let direction = if cannot_refract || is_reflectance { - reflect(&unit_direction, &hit_record.normal) - } else { - refract(&unit_direction, &hit_record.normal, refraction_ratio) - }; - let scattered = ray::Ray::new(hit_record.p, direction); + let direction = + if cannot_refract || reflectance(cos_theta, refraction_ratio) > rng.gen::() { + reflect(unit_direction, hit_record.normal) + } else { + refract(unit_direction, hit_record.normal, refraction_ratio) + }; - Some((attenuation, scattered)) + Some((attenuation, ray::Ray::new(hit_record.p, direction))) } } diff --git a/src/sphere.rs b/src/sphere.rs index 029eb5b..6d8093e 100644 --- a/src/sphere.rs +++ b/src/sphere.rs @@ -2,7 +2,6 @@ use std::ops::Range; use std::sync::Arc; use crate::hittable; -use crate::hittable::length_squared; use crate::hittable::HitRecord; use crate::material; use crate::ray; @@ -31,9 +30,9 @@ impl hittable::Hittable for Sphere { fn hit(&self, ray: &ray::Ray, ray_t: Range) -> Option { let oc = ray.orig - self.center; - let a = length_squared(&ray.dir); + let a = ray.dir.magnitude_squared(); let half_b = oc.dot(&ray.dir); - let c = length_squared(&oc) - self.radius * self.radius; + let c = oc.magnitude_squared() - self.radius * self.radius; let discriminant = half_b * half_b - a * c; if discriminant < 0.0 {