diff options
author | Yann Herklotz <git@yannherklotz.com> | 2020-10-10 00:12:12 +0100 |
---|---|---|
committer | Yann Herklotz <git@yannherklotz.com> | 2020-10-10 00:12:12 +0100 |
commit | 495d1c9c113098f24767b595c7e830f0fa8bc991 (patch) | |
tree | 95eac21eacecadc58f23a59216e764ddd6928979 | |
parent | dd5ac7c5fe336e9cac794cfa72d139613a99b466 (diff) | |
download | leela-495d1c9c113098f24767b595c7e830f0fa8bc991.tar.gz leela-495d1c9c113098f24767b595c7e830f0fa8bc991.zip |
Format all the files and add moving sphere
-rw-r--r-- | Cargo.lock | 75 | ||||
-rw-r--r-- | src/camera.rs | 42 | ||||
-rw-r--r-- | src/colour.rs | 0 | ||||
-rw-r--r-- | src/hittable.rs | 20 | ||||
-rw-r--r-- | src/main.rs | 88 | ||||
-rw-r--r-- | src/material.rs | 39 | ||||
-rw-r--r-- | src/ray.rs | 9 | ||||
-rw-r--r-- | src/render.rs | 72 | ||||
-rw-r--r-- | src/scene.rs | 8 | ||||
-rw-r--r-- | src/sphere.rs | 104 | ||||
-rw-r--r-- | src/utils.rs | 51 |
11 files changed, 358 insertions, 150 deletions
@@ -17,9 +17,9 @@ checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" [[package]] name = "autocfg" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" @@ -54,6 +54,16 @@ dependencies = [ ] [[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] name = "crossbeam-deque" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -70,7 +80,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", "cfg-if", "crossbeam-utils", "lazy_static", @@ -80,31 +90,21 @@ dependencies = [ ] [[package]] -name = "crossbeam-queue" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] name = "crossbeam-utils" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", "cfg-if", "lazy_static", ] [[package]] name = "either" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "fuchsia-cprng" @@ -114,9 +114,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "getrandom" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ "cfg-if", "libc", @@ -125,9 +125,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" dependencies = [ "libc", ] @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.68" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" [[package]] name = "maybe-uninit" @@ -161,20 +161,20 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memoffset" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", ] [[package]] name = "num-traits" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" dependencies = [ - "autocfg 1.0.0", + "autocfg 1.0.1", ] [[package]] @@ -189,9 +189,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" [[package]] name = "rand" @@ -342,10 +342,11 @@ dependencies = [ [[package]] name = "rayon" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270" dependencies = [ + "autocfg 1.0.1", "crossbeam-deque", "either", "rayon-core", @@ -353,12 +354,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" dependencies = [ + "crossbeam-channel", "crossbeam-deque", - "crossbeam-queue", "crossbeam-utils", "lazy_static", "num_cpus", @@ -387,9 +388,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", diff --git a/src/camera.rs b/src/camera.rs index 7564c12..01cb977 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,8 +1,8 @@ -use cgmath::prelude::*; -use cgmath::{Vector3, vec3}; -use rand::prelude::*; use crate::ray::Ray; use crate::utils::random_in_unit_disk; +use cgmath::prelude::*; +use cgmath::{vec3, Vector3}; +use rand::prelude::*; pub struct Camera { origin: Vector3<f64>, @@ -12,12 +12,23 @@ pub struct Camera { u: Vector3<f64>, v: Vector3<f64>, w: Vector3<f64>, - lens_radius: f64 + lens_radius: f64, + time0: f64, + time1: f64, } impl Camera { - pub fn new(lookfrom: &Vector3<f64>, lookat: &Vector3<f64>, vup: &Vector3<f64>, - vfov: f64, aspect: f64, aperture: f64, focus_dist: f64) -> Camera { + pub fn new( + lookfrom: &Vector3<f64>, + lookat: &Vector3<f64>, + vup: &Vector3<f64>, + vfov: f64, + aspect: f64, + aperture: f64, + focus_dist: f64, + time0: f64, + time1: f64, + ) -> Camera { let lens_radius = aperture / 2.0; let half_height = (vfov / 2.0).tan(); let half_width = aspect * half_height; @@ -26,19 +37,28 @@ impl Camera { let v = w.cross(u); Camera { origin: *lookfrom, - lower_left_corner: lookfrom - half_width * focus_dist * u - - half_height * focus_dist * v - focus_dist * w, + lower_left_corner: lookfrom + - half_width * focus_dist * u + - half_height * focus_dist * v + - focus_dist * w, horizontal: 2.0 * half_width * focus_dist * u, vertical: 2.0 * half_height * focus_dist * v, lens_radius, - u, v, w + u, + v, + w, + time0, + time1, } } pub fn get_ray(&self, rng: &mut ThreadRng, s: f64, t: f64) -> Ray { let rd = self.lens_radius * random_in_unit_disk(rng); let offset = self.u * rd.x + self.v * rd.y; - Ray::new(self.origin + offset, - self.lower_left_corner + s * self.horizontal + t * self.vertical - self.origin - offset) + Ray::new( + self.origin + offset, + self.lower_left_corner + s * self.horizontal + t * self.vertical - self.origin - offset, + rng.gen_range(self.time0, self.time1), + ) } } diff --git a/src/colour.rs b/src/colour.rs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/colour.rs diff --git a/src/hittable.rs b/src/hittable.rs index dcc5006..b5a4f20 100644 --- a/src/hittable.rs +++ b/src/hittable.rs @@ -1,22 +1,32 @@ -use cgmath::{Vector3, dot}; use crate::material::Material; use crate::ray::Ray; +use cgmath::{dot, Vector3}; pub struct Hit<'a> { pub t: f64, pub p: Vector3<f64>, pub normal: Vector3<f64>, pub front_face: bool, - pub material: &'a (dyn Material + std::marker::Sync) + pub material: &'a (dyn Material + std::marker::Sync), } impl<'a> Hit<'a> { - pub fn new(ray: &Ray, t: f64, out_normal: Vector3<f64>, material: &'a(dyn Material + std::marker::Sync)) - -> Hit<'a> { + pub fn new( + ray: &Ray, + t: f64, + out_normal: Vector3<f64>, + material: &'a (dyn Material + std::marker::Sync), + ) -> Hit<'a> { let front_face = dot(ray.dir, out_normal) < 0.0; let normal = if front_face { out_normal } else { -out_normal }; let p = ray.at(t); - Hit { t, p, normal, front_face, material } + Hit { + t, + p, + normal, + front_face, + material, + } } } diff --git a/src/main.rs b/src/main.rs index f1a627f..3db5b97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use cgmath::prelude::*; -use cgmath::{Vector3, vec3}; +use cgmath::{vec3, Vector3}; use rand::prelude::*; use std::f64::consts::PI; @@ -16,44 +16,67 @@ pub fn random_scene(rng: &mut ThreadRng) -> scene::Scene { let mut scene = scene::Scene::new(); scene.add(Box::new(sphere::Sphere::new( - vec3(0.0, -1000.0, 0.0), 1000.0, Box::new(material::Lambertian::new(vec3(0.5, 0.5, 0.5)))))); + vec3(0.0, -1000.0, 0.0), + 1000.0, + Box::new(material::Lambertian::new(vec3(0.5, 0.5, 0.5))), + ))); - for a in -11 .. 11 { - for b in -11 .. 11 { + for a in -11..11 { + for b in -11..11 { let choose_mat: f64 = rng.gen(); - let center = vec3(a as f64 + 0.9*rng.gen::<f64>(), 0.2, b as f64 + 0.9*rng.gen::<f64>()); + let center = vec3( + a as f64 + 0.9 * rng.gen::<f64>(), + 0.2, + b as f64 + 0.9 * rng.gen::<f64>(), + ); if (center - vec3(4.0, 0.2, 0.0)).magnitude() > 0.9 { if choose_mat < 0.8 { // diffuse - let albedo = utils::random_vector3(rng, 0.0, 1.0).mul_element_wise(utils::random_vector3(rng, 0.0, 1.0)); - scene.add( - Box::new(sphere::Sphere::new(center, 0.2, Box::new(material::Lambertian::new(albedo))))); + let albedo = utils::random_vector3(rng, 0.0, 1.0) + .mul_element_wise(utils::random_vector3(rng, 0.0, 1.0)); + scene.add(Box::new(sphere::Sphere::new( + center, + 0.2, + Box::new(material::Lambertian::new(albedo)), + ))); } else if choose_mat < 0.95 { // metal let albedo = utils::random_vector3(rng, 0.5, 1.0); let fuzz = rng.gen_range(0.0, 0.5); - scene.add( - Box::new(sphere::Sphere::new( - center, 0.2, Box::new(material::Metal::new(albedo, fuzz))))); + scene.add(Box::new(sphere::Sphere::new( + center, + 0.2, + Box::new(material::Metal::new(albedo, fuzz)), + ))); } else { // glass scene.add(Box::new(sphere::Sphere::new( - center, 0.2, Box::new(material::Dielectric::new(1.5))))); + center, + 0.2, + Box::new(material::Dielectric::new(1.5)), + ))); } } } } scene.add(Box::new(sphere::Sphere::new( - vec3(0.0, 1.0, 0.0), 1.0, Box::new(material::Dielectric::new(1.5))))); + vec3(0.0, 1.0, 0.0), + 1.0, + Box::new(material::Dielectric::new(1.5)), + ))); - scene.add( - Box::new(sphere::Sphere::new(vec3(-4.0, 1.0, 0.0), 1.0, - Box::new(material::Lambertian::new(vec3(0.4, 0.2, 0.1)))))); + scene.add(Box::new(sphere::Sphere::new( + vec3(-4.0, 1.0, 0.0), + 1.0, + Box::new(material::Lambertian::new(vec3(0.4, 0.2, 0.1))), + ))); - scene.add( - Box::new(sphere::Sphere::new( - vec3(4.0, 1.0, 0.0), 1.0, Box::new(material::Metal::new(vec3(0.7, 0.6, 0.5), 0.0))))); + scene.add(Box::new(sphere::Sphere::new( + vec3(4.0, 1.0, 0.0), + 1.0, + Box::new(material::Metal::new(vec3(0.7, 0.6, 0.5), 0.0)), + ))); scene } @@ -61,8 +84,8 @@ pub fn random_scene(rng: &mut ThreadRng) -> scene::Scene { fn main() { let image_width = 400; let image_height = 200; - let samples = 50; - let threads = 1; + let samples = 400; + let threads = 4; let max_depth = 50; let aspect_ratio = image_width as f64 / image_height as f64; @@ -76,10 +99,27 @@ fn main() { let vup = vec3(0.0, 1.0, 0.0); let dist_to_focus = 10.0; let aperture = 0.1; - let camera = camera::Camera::new(&lookfrom, &lookat, &vup, - 20.0 * PI / 180.0, aspect_ratio, aperture, dist_to_focus); + let camera = camera::Camera::new( + &lookfrom, + &lookat, + &vup, + 20.0 * PI / 180.0, + aspect_ratio, + aperture, + dist_to_focus, + 0.0, + 1.0, + ); - render::render(&scene, &camera, image_height, image_width, samples, max_depth, threads); + render::render( + &scene, + &camera, + image_height, + image_width, + samples, + max_depth, + threads, + ); eprintln!("\nDone") } diff --git a/src/material.rs b/src/material.rs index 42e8d0c..91e7c02 100644 --- a/src/material.rs +++ b/src/material.rs @@ -1,16 +1,16 @@ +use crate::hittable::Hit; +use crate::ray::{NextRay, Ray}; +use crate::utils::{random_in_unit_sphere, random_unit_vector, reflect, refract, schlick}; use cgmath::prelude::*; -use cgmath::{Vector3, vec3, dot}; +use cgmath::{dot, vec3, Vector3}; use rand::prelude::*; -use crate::ray::{Ray, NextRay}; -use crate::hittable::Hit; -use crate::utils::{random_unit_vector, random_in_unit_sphere, reflect, refract, schlick}; pub trait Material { fn scatter(&self, rng: &mut ThreadRng, ray: &Ray, hit: &Hit) -> Option<NextRay>; } pub struct Lambertian { - pub albedo: Vector3<f64> + pub albedo: Vector3<f64>, } impl Lambertian { @@ -22,13 +22,16 @@ impl Lambertian { impl Material for Lambertian { fn scatter(&self, rng: &mut ThreadRng, _ray: &Ray, hit: &Hit) -> Option<NextRay> { let scatter_direction = hit.normal + random_unit_vector(rng); - Some(NextRay::new(self.albedo, Ray::new(hit.p, scatter_direction))) + Some(NextRay::new( + self.albedo, + Ray::new(hit.p, scatter_direction, 0.0), + )) } } pub struct Metal { pub albedo: Vector3<f64>, - pub fuzz: f64 + pub fuzz: f64, } impl Metal { @@ -41,7 +44,11 @@ impl Metal { impl Material for Metal { fn scatter(&self, rng: &mut ThreadRng, ray: &Ray, hit: &Hit) -> Option<NextRay> { let reflected = reflect(&ray.dir.normalize(), &hit.normal); - let scattered = Ray::new(hit.p, reflected + self.fuzz * random_in_unit_sphere(rng)); + let scattered = Ray::new( + hit.p, + reflected + self.fuzz * random_in_unit_sphere(rng), + 0.0, + ); if dot(scattered.dir, hit.normal) > 0.0 { Some(NextRay::new(self.albedo, scattered)) } else { @@ -51,7 +58,7 @@ impl Material for Metal { } pub struct Dielectric { - pub ref_idx: f64 + pub ref_idx: f64, } impl Dielectric { @@ -63,24 +70,28 @@ impl Dielectric { impl Material for Dielectric { fn scatter(&self, rng: &mut ThreadRng, ray: &Ray, hit: &Hit) -> Option<NextRay> { let attenuation = vec3(1.0, 1.0, 1.0); - let etai_over_etat = if hit.front_face { 1.0 / self.ref_idx } else { self.ref_idx }; + let etai_over_etat = if hit.front_face { + 1.0 / self.ref_idx + } else { + self.ref_idx + }; let unit_direction = ray.dir.normalize(); let cos_theta = f64::min(dot(-unit_direction, hit.normal), 1.0); - let sin_theta = (1.0 - cos_theta*cos_theta).sqrt(); + let sin_theta = (1.0 - cos_theta * cos_theta).sqrt(); if etai_over_etat * sin_theta > 1.0 { let reflected = reflect(&unit_direction, &hit.normal); - return Some(NextRay::new(attenuation, Ray::new(hit.p, reflected))) + return Some(NextRay::new(attenuation, Ray::new(hit.p, reflected, 0.0))); } let reflect_prob = schlick(cos_theta, etai_over_etat); if rng.gen::<f64>() < reflect_prob { let reflected = reflect(&unit_direction, &hit.normal); - return Some(NextRay::new(attenuation, Ray::new(hit.p, reflected))) + return Some(NextRay::new(attenuation, Ray::new(hit.p, reflected, 0.0))); } let refracted = refract(&unit_direction, &hit.normal, etai_over_etat); - Some(NextRay::new(attenuation, Ray::new(hit.p, refracted))) + Some(NextRay::new(attenuation, Ray::new(hit.p, refracted, 0.0))) } } @@ -2,12 +2,13 @@ use cgmath::Vector3; pub struct Ray { pub orig: Vector3<f64>, - pub dir: Vector3<f64> + pub dir: Vector3<f64>, + pub time: f64, } impl Ray { - pub fn new(orig: Vector3<f64>, dir: Vector3<f64>) -> Ray { - Ray {orig, dir} + pub fn new(orig: Vector3<f64>, dir: Vector3<f64>, time: f64) -> Ray { + Ray { orig, dir, time } } pub fn at(&self, t: f64) -> Vector3<f64> { @@ -17,7 +18,7 @@ impl Ray { pub struct NextRay { pub attenuation: Vector3<f64>, - pub ray: Ray + pub ray: Ray, } impl NextRay { diff --git a/src/render.rs b/src/render.rs index 47c6698..c3d132a 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,20 +1,22 @@ +use crate::camera::Camera; +use crate::hittable::Hittable; +use crate::ray::Ray; +use crate::scene::Scene; +use crate::utils::print_colour; use cgmath::prelude::*; -use cgmath::{Vector3, vec3}; +use cgmath::{vec3, Vector3}; use rand::prelude::*; use rayon::prelude::*; use std::f64::INFINITY; -use crate::camera::Camera; -use crate::scene::Scene; -use crate::utils::{print_colour}; -use crate::ray::Ray; -use crate::hittable::Hittable; fn ray_colour(rng: &mut ThreadRng, ray: &Ray, scene: &Scene, depth: i32) -> Vector3<f64> { - if depth <= 0 { return vec3(0.0, 0.0, 0.0) } + if depth <= 0 { + return vec3(0.0, 0.0, 0.0); + } if let Some(t) = scene.is_hit(ray, 0.001, INFINITY) { if let Some(nray) = t.material.scatter(rng, ray, &t) { - ray_colour(rng, &nray.ray, scene, depth-1).mul_element_wise(nray.attenuation) + ray_colour(rng, &nray.ray, scene, depth - 1).mul_element_wise(nray.attenuation) } else { vec3(0.0, 0.0, 0.0) } @@ -24,10 +26,19 @@ fn ray_colour(rng: &mut ThreadRng, ray: &Ray, scene: &Scene, depth: i32) -> Vect } } -pub fn thread_render(world: &Scene, camera: &Camera, rng: &mut ThreadRng, image_height: i32, - image_width: i32, samples: i32, max_depth: i32, i: i32, j: i32) -> Vector3<f64> { +pub fn thread_render( + world: &Scene, + camera: &Camera, + rng: &mut ThreadRng, + image_height: i32, + image_width: i32, + samples: i32, + max_depth: i32, + i: i32, + j: i32, +) -> Vector3<f64> { let mut colour = vec3(0.0, 0.0, 0.0); - for _ in 0 .. samples { + for _ in 0..samples { let ru: f64 = rng.gen(); let rv: f64 = rng.gen(); let u = (i as f64 + ru) / image_width as f64; @@ -38,19 +49,32 @@ pub fn thread_render(world: &Scene, camera: &Camera, rng: &mut ThreadRng, image_ colour } -pub fn render(world: &Scene, camera: &Camera, image_height: i32, image_width: i32, - samples: i32, max_depth: i32, threads: i32) { - for j in 0 .. image_height { - eprint!("\rScanlines: {:3} / {:3}", j+1, image_height); - for i in 0 .. image_width { - let colours = (0 .. threads) - .into_par_iter() - .map(|_| { - let mut trng = thread_rng(); - thread_render( - world, camera, &mut trng, image_height, image_width, - samples / threads, max_depth, i, j) - }); +pub fn render( + world: &Scene, + camera: &Camera, + image_height: i32, + image_width: i32, + samples: i32, + max_depth: i32, + threads: i32, +) { + for j in 0..image_height { + eprint!("\rScanlines: {:3} / {:3}", j + 1, image_height); + for i in 0..image_width { + let colours = (0..threads).into_par_iter().map(|_| { + let mut trng = thread_rng(); + thread_render( + world, + camera, + &mut trng, + image_height, + image_width, + samples / threads, + max_depth, + i, + j, + ) + }); let colour = colours.sum::<Vector3<f64>>(); print_colour(&colour, samples); } diff --git a/src/scene.rs b/src/scene.rs index 0aa775c..215935a 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,13 +1,15 @@ -use crate::hittable::{Hittable, Hit}; +use crate::hittable::{Hit, Hittable}; use crate::ray::Ray; pub struct Scene { - objects: Vec<Box<dyn Hittable + std::marker::Sync>> + objects: Vec<Box<dyn Hittable + std::marker::Sync>>, } impl Scene { pub fn new() -> Scene { - Scene { objects: Vec::new() } + Scene { + objects: Vec::new(), + } } pub fn add(&mut self, obj: Box<dyn Hittable + std::marker::Sync>) { diff --git a/src/sphere.rs b/src/sphere.rs index f1c88b6..59ca498 100644 --- a/src/sphere.rs +++ b/src/sphere.rs @@ -1,19 +1,27 @@ -use cgmath::prelude::*; -use cgmath::{Vector3, dot}; -use core::borrow::Borrow; +use crate::hittable::{Hit, Hittable}; use crate::material::Material; -use crate::hittable::{Hittable, Hit}; use crate::ray::Ray; +use cgmath::prelude::*; +use cgmath::{dot, Vector3}; +use core::borrow::Borrow; pub struct Sphere { center: Vector3<f64>, radius: f64, - material: Box<dyn Material + std::marker::Sync> + material: Box<dyn Material + std::marker::Sync>, } impl Sphere { - pub fn new(center: Vector3<f64>, radius: f64, material: Box<dyn Material + std::marker::Sync>) -> Sphere { - Sphere { center, radius, material } + pub fn new( + center: Vector3<f64>, + radius: f64, + material: Box<dyn Material + std::marker::Sync>, + ) -> Sphere { + Sphere { + center, + radius, + material, + } } } @@ -23,16 +31,92 @@ impl Hittable for Sphere { let a = ray.dir.magnitude2(); let b = dot(oc, ray.dir); let c = oc.magnitude2() - self.radius * self.radius; - let discriminant = b*b - a*c; + let discriminant = b * b - a * c; + + if discriminant > 0.0 { + let soln = (-b - discriminant.sqrt()) / a; + if soln < t_max && soln > t_min { + return Some(Hit::new( + ray, + soln, + (ray.at(soln) - self.center) / self.radius, + self.material.borrow(), + )); + } + let soln = (-b + discriminant.sqrt()) / a; + if soln < t_max && soln > t_min { + return Some(Hit::new( + ray, + soln, + (ray.at(soln) - self.center) / self.radius, + self.material.borrow(), + )); + } + } + None + } +} + +struct MovingSphere { + center0: Vector3<f64>, + center1: Vector3<f64>, + time0: f64, + time1: f64, + radius: f64, + material: Box<dyn Material + std::marker::Sync>, +} + +impl MovingSphere { + pub fn new( + center0: Vector3<f64>, + center1: Vector3<f64>, + time0: f64, + time1: f64, + radius: f64, + material: Box<dyn Material + std::marker::Sync>, + ) -> MovingSphere { + MovingSphere { + center0, + center1, + time0, + time1, + radius, + material, + } + } + + pub fn center(&self, time: f64) -> Vector3<f64> { + self.center0 + + ((time + self.time0) / (self.time0 + self.time1)) * (self.center1 - self.center0) + } +} + +impl Hittable for MovingSphere { + fn is_hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<Hit> { + let oc = ray.orig - self.center(ray.time); + let a = ray.dir.magnitude2(); + let b = dot(oc, ray.dir); + let c = oc.magnitude2() - self.radius * self.radius; + let discriminant = b * b - a * c; if discriminant > 0.0 { let soln = (-b - discriminant.sqrt()) / a; if soln < t_max && soln > t_min { - return Some(Hit::new(ray, soln, (ray.at(soln) - self.center) / self.radius, self.material.borrow())) + return Some(Hit::new( + ray, + soln, + (ray.at(soln) - self.center(ray.time)) / self.radius, + self.material.borrow(), + )); } let soln = (-b + discriminant.sqrt()) / a; if soln < t_max && soln > t_min { - return Some(Hit::new(ray, soln, (ray.at(soln) - self.center) / self.radius, self.material.borrow())) + return Some(Hit::new( + ray, + soln, + (ray.at(soln) - self.center(ray.time)) / self.radius, + self.material.borrow(), + )); } } None diff --git a/src/utils.rs b/src/utils.rs index 659de7d..7e8c51f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,51 +1,66 @@ use cgmath::prelude::*; -use cgmath::{Vector3, vec3, dot}; +use cgmath::{dot, vec3, Vector3}; use rand::prelude::*; use std::f64::consts::PI; pub fn clamp(x: f64, min: f64, max: f64) -> f64 { - if x < min { min } - else if x > max { max } - else { x } + if x < min { + min + } else if x > max { + max + } else { + x + } } pub fn print_colour(colour: &Vector3<f64>, samples: i32) { let scolour = colour.map(|x| (x / samples as f64).sqrt()); - println!("{} {} {}" - , (256.0 * clamp(scolour.x, 0.0, 0.999)) as i32 - , (256.0 * clamp(scolour.y, 0.0, 0.999)) as i32 - , (256.0 * clamp(scolour.z, 0.0, 0.999)) as i32) + println!( + "{} {} {}", + (256.0 * clamp(scolour.x, 0.0, 0.999)) as i32, + (256.0 * clamp(scolour.y, 0.0, 0.999)) as i32, + (256.0 * clamp(scolour.z, 0.0, 0.999)) as i32 + ) } pub fn random_vector3(rng: &mut ThreadRng, min: f64, max: f64) -> Vector3<f64> { - vec3(rng.gen_range(min, max), rng.gen_range(min, max), rng.gen_range(min, max)) + vec3( + rng.gen_range(min, max), + rng.gen_range(min, max), + rng.gen_range(min, max), + ) } pub fn random_in_unit_sphere(rng: &mut ThreadRng) -> Vector3<f64> { loop { let p = random_vector3(rng, -1.0, 1.0); - if p.magnitude2() <= 1.0 { return p } + if p.magnitude2() <= 1.0 { + return p; + } } } pub fn random_unit_vector(rng: &mut ThreadRng) -> Vector3<f64> { - let a = rng.gen_range(0.0, 2.0*PI); + let a = rng.gen_range(0.0, 2.0 * PI); let z = rng.gen_range(-1.0, 1.0); - let r = ((1.0 - z*z) as f64).sqrt(); - vec3(r*a.cos(), r*a.sin(), z) + let r = ((1.0 - z * z) as f64).sqrt(); + vec3(r * a.cos(), r * a.sin(), z) } pub fn random_in_hemisphere(rng: &mut ThreadRng, normal: &Vector3<f64>) -> Vector3<f64> { let in_unit_sphere = random_in_unit_sphere(rng); - if dot(in_unit_sphere, *normal) > 0.0 { in_unit_sphere } - else { -in_unit_sphere } + if dot(in_unit_sphere, *normal) > 0.0 { + in_unit_sphere + } else { + -in_unit_sphere + } } pub fn random_in_unit_disk(rng: &mut ThreadRng) -> Vector3<f64> { loop { let p = vec3(rng.gen_range(-1.0, 1.0), rng.gen_range(-1.0, 1.0), 0.0); if p.magnitude2() < 1.0 { - return p + return p; } } } @@ -56,13 +71,13 @@ pub fn reflect(v: &Vector3<f64>, n: &Vector3<f64>) -> Vector3<f64> { pub fn refract(uv: &Vector3<f64>, n: &Vector3<f64>, etai_over_etat: f64) -> Vector3<f64> { let cos_theta = dot(-1.0 * uv, *n); - let r_out_parallel = etai_over_etat * (uv + cos_theta*n); + let r_out_parallel = etai_over_etat * (uv + cos_theta * n); let r_out_perp = -(1.0 - r_out_parallel.magnitude2()).sqrt() * n; r_out_parallel + r_out_perp } pub fn schlick(cosine: f64, ref_idx: f64) -> f64 { let r0 = (1.0 - ref_idx) / (1.0 + ref_idx); - let r0sq = r0*r0; + let r0sq = r0 * r0; r0sq + (1.0 - r0) * (1.0 - cosine).powi(5) } |