From a4aeac2b21906975c06390d6945167b99616383f Mon Sep 17 00:00:00 2001 From: Ishan Jain <7921368+ishanjain28@users.noreply.github.com> Date: Sat, 11 Apr 2020 20:25:54 +0530 Subject: [PATCH] Working on adding SIMD, Feature gates 1. Added a gui and default feature gate. 2. Working on acceelarating it with SIMD --- .gitignore | 1 + Cargo.lock | 101 +++++++++++----------- Cargo.toml | 5 +- src/camera.rs | 7 +- src/demos/defocus_blur.rs | 15 ++-- src/demos/dielectric_material.rs | 15 ++-- src/demos/diffuse_materials.rs | 10 ++- src/demos/final_scene.rs | 17 ++-- src/demos/materials.rs | 15 ++-- src/demos/positionable_camera.rs | 15 ++-- src/demos/simple_antialiasing.rs | 5 +- src/main.rs | 68 ++++++++++++--- src/types/material.rs | 30 ++++--- src/types/ray.rs | 2 +- src/types/sphere.rs | 7 +- src/types/vec3.rs | 141 +++++++++++++++++-------------- 16 files changed, 264 insertions(+), 190 deletions(-) diff --git a/.gitignore b/.gitignore index 5a436ef..fd3c431 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ *.ppm *.png +simd.compute diff --git a/Cargo.lock b/Cargo.lock index 270261d..5f1279a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "autocfg" -version = "0.1.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "bitflags" @@ -12,15 +12,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -dependencies = [ - "ppv-lite86", -] - [[package]] name = "cfg-if" version = "0.1.10" @@ -29,24 +20,26 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "crossbeam-deque" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" dependencies = [ "crossbeam-epoch", "crossbeam-utils", + "maybe-uninit", ] [[package]] name = "crossbeam-epoch" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "lazy_static", + "maybe-uninit", "memoffset", "scopeguard", ] @@ -63,9 +56,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ "autocfg", "cfg-if", @@ -91,9 +84,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.6" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" dependencies = [ "libc", ] @@ -106,17 +99,23 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memoffset" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" dependencies = [ - "rustc_version", + "autocfg", ] [[package]] @@ -129,6 +128,15 @@ dependencies = [ "libc", ] +[[package]] +name = "packed_simd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220" +dependencies = [ + "cfg-if", +] + [[package]] name = "ppv-lite86" version = "0.2.6" @@ -146,15 +154,16 @@ dependencies = [ "rand_chacha", "rand_core", "rand_hc", + "rand_pcg", ] [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "c2-chacha", + "ppv-lite86", "rand_core", ] @@ -176,6 +185,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core", +] + [[package]] name = "rayon" version = "1.3.0" @@ -204,25 +222,17 @@ dependencies = [ name = "ria-weekend" version = "0.1.0" dependencies = [ + "packed_simd", "rand", "rayon", "sdl2", ] -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sdl2" @@ -246,21 +256,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 19c8102..656513f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ authors = ["ishanjain28 "] edition = "2018" [dependencies] -sdl2 = "0.33.0" -rand = "0.7.3" +sdl2 = { version = "0.33.0", optional = true } +rand = { version = "0.7.3", features = ["small_rng"] } rayon = "1.3.0" +packed_simd = "0.3.3" diff --git a/src/camera.rs b/src/camera.rs index abd8bf3..1b2483d 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -61,9 +61,8 @@ impl Camera { } } - pub fn get_ray(&self, u: f64, v: f64) -> Ray { - let mut rng = rand::thread_rng(); - let rd = random_in_unit_disk(&mut rng) * self.lens_radius; + pub fn get_ray(&self, u: f64, v: f64, rng: &mut R) -> Ray { + let rd = random_in_unit_disk(rng) * self.lens_radius; let offset = self.u * rd.x() + self.v * rd.y(); Ray::new( self.origin + offset, @@ -72,7 +71,7 @@ impl Camera { } } -fn random_in_unit_disk(rng: &mut rand::rngs::ThreadRng) -> Vec3 { +fn random_in_unit_disk(rng: &mut R) -> Vec3 { let mut p = Vec3::new(rng.gen::(), rng.gen::(), 0.0) * 2.0 - Vec3::new(1.0, 1.0, 0.0); while p.dot(&p) >= 1.0 { diff --git a/src/demos/defocus_blur.rs b/src/demos/defocus_blur.rs index e684af5..4939bc6 100644 --- a/src/demos/defocus_blur.rs +++ b/src/demos/defocus_blur.rs @@ -7,7 +7,7 @@ use { }, Camera, }, - rand::Rng, + rand::{rngs::SmallRng, Rng, SeedableRng}, }; pub struct DefocusBlur; @@ -82,6 +82,7 @@ impl Demo for DefocusBlur { let world = world.unwrap(); let mut rng = rand::thread_rng(); + let mut rng = SmallRng::from_rng(&mut rng).unwrap(); let mut offset = 0; for j in start_y..start_y + ny { @@ -91,8 +92,8 @@ impl Demo for DefocusBlur { let u = (i as f64 + rng.gen::()) / x as f64; let v = (j as f64 + rng.gen::()) / y as f64; - let ray = camera.get_ray(u, v); - color += calc_color(ray, &world, 0); + let ray = camera.get_ray(u, v, &mut rng); + color += calc_color(ray, &world, 0, &mut rng); } color /= samples as f64; @@ -107,14 +108,16 @@ impl Demo for DefocusBlur { } } -fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 { +fn calc_color(ray: Ray, world: &HitableList, depth: u32, rng: &mut SmallRng) -> Vec3 { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) { if depth >= 50 { Vec3::new(0.0, 0.0, 0.0) } else { let material = hit_rec.material.as_ref(); - if let (attenuation, Some(scattered_ray)) = material.unwrap().scatter(&ray, &hit_rec) { - calc_color(scattered_ray, &world, depth + 1) * attenuation + if let (attenuation, Some(scattered_ray)) = + material.unwrap().scatter(&ray, &hit_rec, rng) + { + calc_color(scattered_ray, &world, depth + 1, rng) * attenuation } else { Vec3::new(0.0, 0.0, 0.0) } diff --git a/src/demos/dielectric_material.rs b/src/demos/dielectric_material.rs index 2f369db..c701170 100644 --- a/src/demos/dielectric_material.rs +++ b/src/demos/dielectric_material.rs @@ -7,7 +7,7 @@ use { }, Camera, }, - rand::Rng, + rand::{rngs::SmallRng, Rng, SeedableRng}, }; pub struct DielectricMaterial; @@ -69,6 +69,7 @@ impl Demo for DielectricMaterial { let world = world.unwrap(); let mut rng = rand::thread_rng(); + let mut rng = SmallRng::from_rng(&mut rng).unwrap(); let mut offset = 0; for j in start_y..start_y + ny { @@ -78,8 +79,8 @@ impl Demo for DielectricMaterial { let u = (i as f64 + rng.gen::()) / x as f64; let v = (j as f64 + rng.gen::()) / y as f64; - let ray = camera.get_ray(u, v); - color += calc_color(ray, &world, 0); + let ray = camera.get_ray(u, v, &mut rng); + color += calc_color(ray, &world, 0, &mut rng); } color /= samples as f64; @@ -94,14 +95,16 @@ impl Demo for DielectricMaterial { } } -fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 { +fn calc_color(ray: Ray, world: &HitableList, depth: u32, rng: &mut SmallRng) -> Vec3 { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) { if depth >= 50 { Vec3::new(0.0, 0.0, 0.0) } else { let material = hit_rec.material.as_ref(); - if let (attenuation, Some(scattered_ray)) = material.unwrap().scatter(&ray, &hit_rec) { - calc_color(scattered_ray, &world, depth + 1) * attenuation + if let (attenuation, Some(scattered_ray)) = + material.unwrap().scatter(&ray, &hit_rec, rng) + { + calc_color(scattered_ray, &world, depth + 1, rng) * attenuation } else { Vec3::new(0.0, 0.0, 0.0) } diff --git a/src/demos/diffuse_materials.rs b/src/demos/diffuse_materials.rs index 4e780a6..288834e 100644 --- a/src/demos/diffuse_materials.rs +++ b/src/demos/diffuse_materials.rs @@ -4,7 +4,7 @@ use { types::{Hitable, HitableList, Ray, Sphere, Vec3}, Camera, }, - rand::Rng, + rand::{rngs::SmallRng, Rng, SeedableRng}, }; pub struct DiffuseMaterials; @@ -43,7 +43,9 @@ impl Demo for DiffuseMaterials { let world = world.unwrap(); let mut rng = rand::thread_rng(); + let mut rng = SmallRng::from_rng(&mut rng).unwrap(); let mut offset = 0; + for j in start_y..start_y + ny { for i in start_x..start_x + nx { let mut color = Vec3::new(0.0, 0.0, 0.0); @@ -51,7 +53,7 @@ impl Demo for DiffuseMaterials { for _s in 0..samples { let u = (i as f64 + rng.gen::()) / x as f64; let v = (j as f64 + rng.gen::()) / y as f64; - let r = camera.get_ray(u, v); + let r = camera.get_ray(u, v, &mut rng); color += calc_color(r, &world, &mut rng); } @@ -72,7 +74,7 @@ impl Demo for DiffuseMaterials { } } -fn calc_color(ray: Ray, world: &HitableList, rng: &mut rand::rngs::ThreadRng) -> Vec3 { +fn calc_color(ray: Ray, world: &HitableList, rng: &mut SmallRng) -> Vec3 { // The value of t_min here could've been 0.0 but since f32/f64 can only be // partially compared, It may cause shadow acne effect. // To combat this problem, We set a bias @@ -87,7 +89,7 @@ fn calc_color(ray: Ray, world: &HitableList, rng: &mut rand::rngs::ThreadRng) -> } } -fn random_point_in_unit_sphere(rng: &mut rand::rngs::ThreadRng) -> Vec3 { +fn random_point_in_unit_sphere(rng: &mut R) -> Vec3 { let mut point = Vec3::new(rng.gen::(), rng.gen::(), rng.gen::()) * 2.0 - Vec3::new(1.0, 1.0, 1.0); while point.sq_len() >= 1.0 { diff --git a/src/demos/final_scene.rs b/src/demos/final_scene.rs index 4067c11..a1e41b7 100644 --- a/src/demos/final_scene.rs +++ b/src/demos/final_scene.rs @@ -7,7 +7,7 @@ use { }, Camera, }, - rand::Rng, + rand::{rngs::SmallRng, Rng, SeedableRng}, }; pub struct FinalScene; @@ -131,6 +131,7 @@ impl Demo for FinalScene { let world = world.unwrap(); let mut rng = rand::thread_rng(); + let mut rng = SmallRng::from_rng(&mut rng).unwrap(); let mut offset = 0; for j in start_y..start_y + ny { @@ -140,8 +141,8 @@ impl Demo for FinalScene { let u = (i as f64 + rng.gen::()) / x as f64; let v = (j as f64 + rng.gen::()) / y as f64; - let ray = camera.get_ray(u, v); - color += calc_color(ray, &world, 0); + let ray = camera.get_ray(u, v, &mut rng); + color += calc_color(ray, &world, 0, &mut rng); } color /= samples as f64; @@ -156,14 +157,16 @@ impl Demo for FinalScene { } } -fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 { +fn calc_color(ray: Ray, world: &HitableList, depth: u32, rng: &mut SmallRng) -> Vec3 { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) { - if depth >= 50 { + if depth >= 10 { Vec3::new(0.0, 0.0, 0.0) } else { let material = hit_rec.material.as_ref(); - if let (attenuation, Some(scattered_ray)) = material.unwrap().scatter(&ray, &hit_rec) { - calc_color(scattered_ray, &world, depth + 1) * attenuation + if let (attenuation, Some(scattered_ray)) = + material.unwrap().scatter(&ray, &hit_rec, rng) + { + calc_color(scattered_ray, &world, depth + 1, rng) * attenuation } else { Vec3::new(0.0, 0.0, 0.0) } diff --git a/src/demos/materials.rs b/src/demos/materials.rs index e28ac9d..e1ff02d 100644 --- a/src/demos/materials.rs +++ b/src/demos/materials.rs @@ -7,7 +7,7 @@ use { }, Camera, }, - rand::Rng, + rand::{rngs::SmallRng, Rng, SeedableRng}, }; pub struct Materials; @@ -63,6 +63,7 @@ impl Demo for Materials { let camera = camera.unwrap(); let mut rng = rand::thread_rng(); + let mut rng = SmallRng::from_rng(&mut rng).unwrap(); let mut offset = 0; for j in start_y..start_y + ny { @@ -72,8 +73,8 @@ impl Demo for Materials { let u = (i as f64 + rng.gen::()) / x as f64; let v = (j as f64 + rng.gen::()) / y as f64; - let ray = camera.get_ray(u, v); - color += calc_color(ray, world.unwrap(), 0); + let ray = camera.get_ray(u, v, &mut rng); + color += calc_color(ray, world.unwrap(), 0, &mut rng); } color /= samples as f64; @@ -88,14 +89,16 @@ impl Demo for Materials { } } -fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 { +fn calc_color(ray: Ray, world: &HitableList, depth: u32, rng: &mut SmallRng) -> Vec3 { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) { if depth >= 50 { Vec3::new(0.0, 0.0, 0.0) } else { let material = hit_rec.material.as_ref(); - if let (attenuation, Some(scattered_ray)) = material.unwrap().scatter(&ray, &hit_rec) { - calc_color(scattered_ray, &world, depth + 1) * attenuation + if let (attenuation, Some(scattered_ray)) = + material.unwrap().scatter(&ray, &hit_rec, rng) + { + calc_color(scattered_ray, &world, depth + 1, rng) * attenuation } else { Vec3::new(0.0, 0.0, 0.0) } diff --git a/src/demos/positionable_camera.rs b/src/demos/positionable_camera.rs index efa2ba8..2097561 100644 --- a/src/demos/positionable_camera.rs +++ b/src/demos/positionable_camera.rs @@ -7,7 +7,7 @@ use { }, Camera, }, - rand::Rng, + rand::{rngs::SmallRng, Rng, SeedableRng}, }; pub struct PositionableCamera; @@ -78,6 +78,7 @@ impl Demo for PositionableCamera { let camera = camera.unwrap(); let world = world.unwrap(); let mut rng = rand::thread_rng(); + let mut rng = SmallRng::from_rng(&mut rng).unwrap(); let mut offset = 0; for j in start_y..start_y + ny { @@ -87,8 +88,8 @@ impl Demo for PositionableCamera { let u = (i as f64 + rng.gen::()) / x as f64; let v = (j as f64 + rng.gen::()) / y as f64; - let ray = camera.get_ray(u, v); - color += calc_color(ray, &world, 0); + let ray = camera.get_ray(u, v, &mut rng); + color += calc_color(ray, &world, 0, &mut rng); } color /= samples as f64; // gamma 2 corrected @@ -101,14 +102,16 @@ impl Demo for PositionableCamera { } } -fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 { +fn calc_color(ray: Ray, world: &HitableList, depth: u32, rng: &mut SmallRng) -> Vec3 { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) { if depth >= 50 { Vec3::new(0.0, 0.0, 0.0) } else { let material = hit_rec.material.as_ref(); - if let (attenuation, Some(scattered_ray)) = material.unwrap().scatter(&ray, &hit_rec) { - calc_color(scattered_ray, &world, depth + 1) * attenuation + if let (attenuation, Some(scattered_ray)) = + material.unwrap().scatter(&ray, &hit_rec, rng) + { + calc_color(scattered_ray, &world, depth + 1, rng) * attenuation } else { Vec3::new(0.0, 0.0, 0.0) } diff --git a/src/demos/simple_antialiasing.rs b/src/demos/simple_antialiasing.rs index e694256..f21a15e 100644 --- a/src/demos/simple_antialiasing.rs +++ b/src/demos/simple_antialiasing.rs @@ -4,7 +4,7 @@ use { types::{Hitable, HitableList, Ray, Sphere, Vec3}, Camera, }, - rand::Rng, + rand::{rngs::SmallRng, Rng, SeedableRng}, }; pub struct SimpleAntialiasing; @@ -41,6 +41,7 @@ impl Demo for SimpleAntialiasing { let camera = camera.unwrap(); let mut rng = rand::thread_rng(); + let mut rng = SmallRng::from_rng(&mut rng).unwrap(); let mut offset = 0; for j in start_y..start_y + ny { @@ -50,7 +51,7 @@ impl Demo for SimpleAntialiasing { let u = (i as f64 + rng.gen::()) / x as f64; let v = (j as f64 + rng.gen::()) / y as f64; - let r = camera.get_ray(u, v); + let r = camera.get_ray(u, v, &mut rng); color += calc_color(r, world.unwrap()); } color /= samples as f64; diff --git a/src/main.rs b/src/main.rs index bd6daa1..0cce323 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,25 +6,28 @@ mod types; pub use camera::Camera; -use { - demos::Demo, - sdl2::{ - event::{Event, WindowEvent}, - keyboard::Keycode, - pixels::PixelFormatEnum, - }, - std::time::Instant, -}; +use {demos::Demo, std::time::Instant}; const NUM_SAMPLES: u8 = 100; const VERTICAL_PARTITION: usize = 8; const HORIZONTAL_PARTITION: usize = 8; +const WIDTH: usize = 800; +const HEIGHT: usize = 400; fn main() -> Result<(), String> { + run(WIDTH, HEIGHT) +} + +#[cfg(feature = "gui")] +fn run(mut width: usize, mut height: usize) -> Result<(), String> { + use sdl2::{ + event::{Event, WindowEvent}, + keyboard::Keycode, + pixels::PixelFormatEnum, + }; + let sdl_ctx = sdl2::init()?; let video_subsys = sdl_ctx.video()?; - let (mut width, mut height): (usize, usize) = (1600, 800); - let window = video_subsys .window("Ray tracing in a weekend", width as u32, height as u32) .position_centered() @@ -49,7 +52,7 @@ fn main() -> Result<(), String> { //println!("{:?} {:?} {:?}", texture.query(), texture.color_mod(), texture.alpha_mod()); - let mut active_demo: &dyn Demo = &demos::SimpleRectangle; + let active_demo: &dyn Demo = &demos::MotionBlur; // TODO: Should update when window is unfocus since the project window retains // data from overlapped window // TODO: Maybe consider using condition variable to make loop {} not run at full @@ -118,8 +121,6 @@ fn main() -> Result<(), String> { active_demo = &demos::FinalScene; should_update = true; } - None => unreachable!(), - _ => (), }; } Event::Window { @@ -153,3 +154,42 @@ fn main() -> Result<(), String> { } } } + +#[cfg(not(feature = "gui"))] +fn run(width: usize, height: usize) -> Result<(), String> { + let mut buffer = vec![0; width * height * 4]; + let list_of_demos: [Box; 12] = [ + Box::new(demos::SimpleRectangle), + Box::new(demos::LinearGradientRectangle), + Box::new(demos::SimpleSphere), + Box::new(demos::SurfaceNormalSphere), + Box::new(demos::HitableSphere), + Box::new(demos::SimpleAntialiasing), + Box::new(demos::DiffuseMaterials), + Box::new(demos::Materials), + Box::new(demos::DielectricMaterial), + Box::new(demos::PositionableCamera), + Box::new(demos::DefocusBlur), + Box::new(demos::FinalScene), + ]; + for demo in list_of_demos.iter() { + println!( + "Starting {} at {}x{} with {} samples", + demo.name(), + width, + height, + NUM_SAMPLES + ); + let now = Instant::now(); + demo.render(&mut buffer, width, height, NUM_SAMPLES); + println!( + "Rendered Demo {}. Time Taken(s) = {}", + demo.name(), + now.elapsed().as_secs_f64() + ); + + demo.save_as_ppm(&buffer, width, height); + } + + Ok(()) +} diff --git a/src/types/material.rs b/src/types/material.rs index f9674fb..0f2bb47 100644 --- a/src/types/material.rs +++ b/src/types/material.rs @@ -1,10 +1,10 @@ use { crate::types::{HitRecord, Ray, Vec3}, - rand::Rng, + rand::{rngs::SmallRng, Rng}, }; pub trait Material: Send + Sync { - fn scatter(&self, ray: &Ray, hit_rec: &HitRecord) -> (Vec3, Option); + fn scatter(&self, ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option); } pub struct Lambertian { @@ -18,9 +18,8 @@ impl Lambertian { } impl Material for Lambertian { - fn scatter(&self, _ray: &Ray, hit_rec: &HitRecord) -> (Vec3, Option) { - let mut rng = rand::thread_rng(); - let target = hit_rec.p + hit_rec.normal + random_point_in_unit_sphere(&mut rng); + fn scatter(&self, _ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option) { + let target = hit_rec.p + hit_rec.normal + random_point_in_unit_sphere(rng); let scattered_ray = Ray::new(hit_rec.p, target - hit_rec.p); (self.albedo, Some(scattered_ray)) @@ -42,13 +41,16 @@ impl Metal { } impl Material for Metal { - fn scatter(&self, ray_in: &Ray, hit_rec: &HitRecord) -> (Vec3, Option) { - let mut rng = rand::thread_rng(); - + fn scatter( + &self, + ray_in: &Ray, + hit_rec: &HitRecord, + rng: &mut SmallRng, + ) -> (Vec3, Option) { let reflected_ray = reflect(ray_in.direction().unit_vector(), hit_rec.normal); let scattered_ray = Ray::new( hit_rec.p, - reflected_ray + random_point_in_unit_sphere(&mut rng) * self.fuzz, + reflected_ray + random_point_in_unit_sphere(rng) * self.fuzz, ); if scattered_ray.direction().dot(&hit_rec.normal) > 0.0 { @@ -70,11 +72,15 @@ impl Dielectric { } impl Material for Dielectric { - fn scatter(&self, ray_in: &Ray, hit_rec: &HitRecord) -> (Vec3, Option) { + fn scatter( + &self, + ray_in: &Ray, + hit_rec: &HitRecord, + rng: &mut SmallRng, + ) -> (Vec3, Option) { let reflected_ray = reflect(ray_in.direction(), hit_rec.normal); // Glass absorbs nothing! So, Attenuation is always going to be 1.0 for this let attenuation = Vec3::new(1.0, 1.0, 1.0); - let mut rng = rand::thread_rng(); let (outward_normal, ni_over_nt, cosine) = if ray_in.direction().dot(&hit_rec.normal) > 0.0 { @@ -130,7 +136,7 @@ fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f64) -> Option { } } -fn random_point_in_unit_sphere(rng: &mut rand::rngs::ThreadRng) -> Vec3 { +fn random_point_in_unit_sphere(rng: &mut R) -> Vec3 { let mut point = Vec3::new(rng.gen::(), rng.gen::(), rng.gen::()) * 2.0 - Vec3::new(1.0, 1.0, 1.0); while point.sq_len() >= 1.0 { diff --git a/src/types/ray.rs b/src/types/ray.rs index 4075357..a54d729 100644 --- a/src/types/ray.rs +++ b/src/types/ray.rs @@ -6,7 +6,7 @@ pub struct Ray { } impl Ray { - pub fn new(a: Vec3, b: Vec3) -> Ray { + pub const fn new(a: Vec3, b: Vec3) -> Ray { Ray { a, b } } #[inline] diff --git a/src/types/sphere.rs b/src/types/sphere.rs index 417f4a9..efa2e9b 100644 --- a/src/types/sphere.rs +++ b/src/types/sphere.rs @@ -24,6 +24,7 @@ impl Sphere { } impl Hitable for Sphere { + #[inline] fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option { let oc = ray.origin() - self.center; let a = ray.direction().dot(&ray.direction()); @@ -38,9 +39,9 @@ impl Hitable for Sphere { // https://vchizhov.github.io/resources/ray%20tracing/ray%20tracing%20tutorial%20series%20vchizhov/ray_casting/part1/intersecting_a_sphere.md.html#appendix let discriminant = b * b - a * c; let discriminant_root = discriminant.sqrt(); - + let a_d = 1.0 / a; if discriminant > 0.0 { - let root = (-b - discriminant_root) / a; + let root = (-b - discriminant_root) * a_d; if root < t_max && root > t_min { let p = ray.point_at_parameter(root); return Some(HitRecord { @@ -51,7 +52,7 @@ impl Hitable for Sphere { }); } - let root = (-b + discriminant_root) / a; + let root = (-b + discriminant_root) * a_d; if root < t_max && root > t_min { let p = ray.point_at_parameter(root); diff --git a/src/types/vec3.rs b/src/types/vec3.rs index 12d931e..b541199 100644 --- a/src/types/vec3.rs +++ b/src/types/vec3.rs @@ -1,39 +1,45 @@ -use std::{ - fmt::{Display, Formatter, Result as FmtResult}, - ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign}, +use { + packed_simd::f64x4, + std::{ + fmt::{Display, Formatter, Result as FmtResult}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, + }, }; -#[derive(Copy, Clone)] -pub struct Vec3([f64; 3]); +// x,y,z,t +// r,g,b,_ +#[derive(Debug, PartialEq, Copy, Clone)] +pub struct Vec3(f64x4); impl Vec3 { #[inline] pub const fn new(a: f64, b: f64, c: f64) -> Vec3 { - Vec3([a, b, c]) + Vec3(f64x4::new(a, b, c, 0.0)) } + #[inline] pub fn x(&self) -> f64 { - self[0] + self.0.extract(0) } #[inline] pub fn y(&self) -> f64 { - self[1] + self.0.extract(1) } #[inline] pub fn z(&self) -> f64 { - self[2] + self.0.extract(2) } #[inline] pub fn r(&self) -> f64 { - self[0] + self.0.extract(0) } #[inline] pub fn g(&self) -> f64 { - self[1] + self.0.extract(1) } #[inline] pub fn b(&self) -> f64 { - self[2] + self.0.extract(2) } #[inline] @@ -43,35 +49,33 @@ impl Vec3 { #[inline] pub fn sq_len(&self) -> f64 { - self[0] * self[0] + self[1] * self[1] + self[2] * self[2] + let p = self.0.powf(f64x4::new(2.0, 2.0, 2.0, 2.0)); + p.sum() } #[inline] pub fn dot(&self, v: &Vec3) -> f64 { - self[0] * v[0] + self[1] * v[1] + self[2] * v[2] + let p = self.0 * v.0; + p.sum() } #[inline] pub fn cross(&self, v: &Vec3) -> Vec3 { - Vec3([ - self[1] * v[2] - self[2] * v[1], - self[2] * v[0] - self[0] * v[2], - self[0] * v[1] - self[1] * v[0], - ]) - } + let p1 = self.0 * f64x4::new(v.0.extract(1), v.0.extract(2), v.0.extract(0), 0.0); + let p2 = self.0 * f64x4::new(v.0.extract(2), v.0.extract(0), v.0.extract(1), 0.0); - #[inline] - pub fn make_unit_vector(&mut self) { - let k = 1.0f64 / (self[0] * self[0] + self[1] * self[1] + self[2] * self[2]); - self[0] *= k; - self[1] *= k; - self[2] *= k; + Vec3(f64x4::new( + p1.extract(1) - p2.extract(2), + p1.extract(2) - p2.extract(0), + p1.extract(0) - p2.extract(1), + 0.0, + )) } #[inline] pub fn unit_vector(&self) -> Vec3 { let length = self.length(); - Vec3([self[0] / length, self[1] / length, self[2] / length]) + Vec3(self.0 / length) } } @@ -79,15 +83,13 @@ impl Add for Vec3 { type Output = Vec3; fn add(self, o: Vec3) -> Vec3 { - Vec3([self[0] + o[0], self[1] + o[1], self[2] + o[2]]) + Vec3(self.0 + o.0) } } impl AddAssign for Vec3 { fn add_assign(&mut self, o: Vec3) { - self.0[0] += o.0[0]; - self.0[1] += o.0[1]; - self.0[2] += o.0[2]; + self.0 += o.0 } } @@ -95,15 +97,13 @@ impl Sub for Vec3 { type Output = Vec3; fn sub(self, o: Vec3) -> Vec3 { - Vec3([self[0] - o[0], self[1] - o[1], self[2] - o[2]]) + Vec3(self.0 - o.0) } } impl SubAssign for Vec3 { fn sub_assign(&mut self, o: Vec3) { - self[0] -= o[0]; - self[1] -= o[1]; - self[2] -= o[2]; + self.0 -= o.0 } } @@ -111,37 +111,33 @@ impl Neg for Vec3 { type Output = Vec3; fn neg(self) -> Vec3 { - Vec3([-self[0], -self[1], -self[2]]) + Vec3(-self.0) } } impl MulAssign for Vec3 { fn mul_assign(&mut self, o: Vec3) { - self[0] *= o[0]; - self[1] *= o[1]; - self[2] *= o[2]; + self.0 *= o.0 } } impl MulAssign for Vec3 { fn mul_assign(&mut self, o: f64) { - self[0] *= o; - self[1] *= o; - self[2] *= o; + self.0 *= o } } impl Mul for Vec3 { type Output = Vec3; fn mul(self, o: f64) -> Vec3 { - Vec3([self[0] * o, self[1] * o, self[2] * o]) + Vec3(self.0 * o) } } impl Mul for Vec3 { type Output = Vec3; fn mul(self, o: Vec3) -> Vec3 { - Vec3([self[0] * o[0], self[1] * o[1], self[2] * o[2]]) + Vec3(self.0 * o.0) } } @@ -149,7 +145,7 @@ impl Div for Vec3 { type Output = Vec3; fn div(self, o: Vec3) -> Vec3 { - Vec3([self[0] / o[0], self[1] / o[1], self[2] / o[2]]) + Vec3(self.0 / o.0) } } @@ -158,35 +154,52 @@ impl Div for Vec3 { fn div(self, o: f64) -> Vec3 { let o = 1.0 / o; - Vec3([self[0] * o, self[1] * o, self[2] * o]) + self * o } } impl DivAssign for Vec3 { fn div_assign(&mut self, o: f64) { let o = 1.0 / o; - self.0[0] *= o; - self.0[1] *= o; - self.0[2] *= o; - } -} - -impl Index for Vec3 { - type Output = f64; - - fn index(&self, q: usize) -> &f64 { - &self.0[q] - } -} - -impl IndexMut for Vec3 { - fn index_mut(&mut self, q: usize) -> &mut f64 { - &mut self.0[q] + *self *= o; } } impl Display for Vec3 { fn fmt(&self, f: &mut Formatter) -> FmtResult { - f.write_fmt(format_args!("{} {} {}", self[0], self[1], self[2])) + //f.write_fmt(format_args!("{} {} {}", self[0], self[1], self[2])) + Ok(()) } } + +#[test] +fn vec3_test() { + let v = Vec3::new(0.5, 0.6, 0.8); + let q = Vec3::new(0.4, 0.2, 0.1); + let cross = Vec3::new( + -0.10000000000000003, + 0.2700000000000001, + -0.13999999999999999, + ); + let unit_vector = Vec3::new(0.4472135954999579, 0.5366563145999494, 0.7155417527999327); + let add = Vec3::new(0.9, 0.8, 0.9); + let sub = Vec3::new(0.09999999999999998, 0.39999999999999997, 0.7000000000000001); + let mul = Vec3::new(0.2, 0.12, 0.08000000000000002); + let div = Vec3::new(1.25, 2.9999999999999996, 8.0); + + assert_eq!(v.x(), 0.5); + assert_eq!(v.y(), 0.6); + assert_eq!(v.z(), 0.8); + assert_eq!(v.r(), 0.5); + assert_eq!(v.g(), 0.6); + assert_eq!(v.b(), 0.8); + assert_eq!(v.length(), 1.118033988749895); + assert_eq!(v.sq_len(), 1.25); + assert_eq!(v.dot(&q), 0.4); + assert_eq!(v.cross(&q), cross); + assert_eq!(v.unit_vector(), unit_vector); + assert_eq!(v + q, add); + assert_eq!(v - q, sub); + assert_eq!(v * q, mul); + assert_eq!(v / q, div); +}