From 0da70546cda8c1383f34dcb60eabc4b4157aaeb4 Mon Sep 17 00:00:00 2001 From: ishanjain28 Date: Thu, 12 Mar 2020 21:17:39 +0530 Subject: [PATCH] Added Defocus Blur and Final Scene, Some refactors 1. Added Defocus Blur Scene and the Final Scene 2. Refactored how the fields from `Chunk` were referenced in `render_chunk` methods 3. Fixed a issue in camera where it remained out of focus regardless of focus distance value 4. Fixed a issue in random_in_unit_disc where it kept generating random nums in a weird space than a disc --- src/camera.rs | 6 +- src/demos/defocus_blur.rs | 20 +-- src/demos/dielectric_material.rs | 16 ++- src/demos/diffuse_materials.rs | 16 ++- src/demos/final_scene.rs | 177 +++++++++++++++++++++++++ src/demos/hitable_sphere.rs | 16 ++- src/demos/linear_gradient_rectangle.rs | 16 ++- src/demos/materials.rs | 16 ++- src/demos/mod.rs | 3 +- src/demos/positionable_camera.rs | 16 ++- src/demos/simple_antialiasing.rs | 16 ++- src/demos/simple_rectangle.rs | 17 +-- src/demos/simple_sphere.rs | 17 +-- src/demos/surface_normal_sphere.rs | 17 +-- src/main.rs | 4 +- src/types/hitable_list.rs | 6 + 16 files changed, 291 insertions(+), 88 deletions(-) create mode 100644 src/demos/final_scene.rs diff --git a/src/camera.rs b/src/camera.rs index e560ff1..abd8bf3 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -45,8 +45,8 @@ impl Camera { - u * focus_distance * half_width - v * focus_distance * half_height - w * focus_distance; - let horizontal = u * half_width * 2.0; - let vertical = v * half_height * 2.0; + let horizontal = u * half_width * focus_distance * 2.0; + let vertical = v * half_height * focus_distance * 2.0; let lens_radius = aperture / 2.0; Self { @@ -73,7 +73,7 @@ impl Camera { } fn random_in_unit_disk(rng: &mut rand::rngs::ThreadRng) -> Vec3 { - let mut p = Vec3::new(rng.gen::(), rng.gen::(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0); + 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 { p = Vec3::new(rng.gen::(), rng.gen::(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0); diff --git a/src/demos/defocus_blur.rs b/src/demos/defocus_blur.rs index 9c7afbc..e684af5 100644 --- a/src/demos/defocus_blur.rs +++ b/src/demos/defocus_blur.rs @@ -39,14 +39,14 @@ impl Demo for DefocusBlur { Box::new(Sphere::with_material( Vec3::new(0.0, -100.5, -1.0), 100.0, - Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.0))), + Box::new(Lambertian::new(Vec3::new(0.2, 0.2, 0.6))), )), ], }) } fn camera(&self, aspect_ratio: f64) -> Option { - let lookfrom = Vec3::new(3.0, 3.0, 2.0); + let lookfrom = Vec3::new(3.0, 3.0, 3.0); let lookat = Vec3::new(0.0, 0.0, -1.0); let aperture = 2.0; let distance_to_focus = (lookfrom - lookat).length(); @@ -69,13 +69,15 @@ impl Demo for DefocusBlur { world: Option<&HitableList>, samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; let camera = camera.unwrap(); let world = world.unwrap(); diff --git a/src/demos/dielectric_material.rs b/src/demos/dielectric_material.rs index 4ea9107..2f369db 100644 --- a/src/demos/dielectric_material.rs +++ b/src/demos/dielectric_material.rs @@ -56,13 +56,15 @@ impl Demo for DielectricMaterial { world: Option<&HitableList>, samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; let camera = camera.unwrap(); let world = world.unwrap(); diff --git a/src/demos/diffuse_materials.rs b/src/demos/diffuse_materials.rs index c81589a..4e780a6 100644 --- a/src/demos/diffuse_materials.rs +++ b/src/demos/diffuse_materials.rs @@ -30,13 +30,15 @@ impl Demo for DiffuseMaterials { world: Option<&HitableList>, samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; let camera = camera.unwrap(); let world = world.unwrap(); diff --git a/src/demos/final_scene.rs b/src/demos/final_scene.rs new file mode 100644 index 0000000..60d1b76 --- /dev/null +++ b/src/demos/final_scene.rs @@ -0,0 +1,177 @@ +use { + crate::{ + demos::{Chunk, Demo}, + types::{ + material::{Dielectric, Lambertian, Metal}, + Hitable, HitableList, Ray, Sphere, Vec3, + }, + Camera, + }, + rand::Rng, +}; + +pub struct FinalScene; + +impl Demo for FinalScene { + fn name(&self) -> &'static str { + "final-scene" + } + + fn world(&self) -> Option { + let mut world = HitableList { + list: Vec::with_capacity(500), + }; + + world.push(Box::new(Sphere::with_material( + Vec3::new(0.0, -1000.0, 0.0), + 1000.0, + Box::new(Lambertian::new(Vec3::new(0.5, 0.5, 0.5))), + ))); + + let mut rng = rand::thread_rng(); + let radius = 0.2; + let l = Vec3::new(4.0, 0.2, 0.0); + + for a in -11..11 { + let a = a as f64; + for b in -11..11 { + let b = b as f64; + let choose_material_probability = rng.gen::(); + let center = Vec3::new(a + 0.9 * rng.gen::(), 0.2, b + 0.9 * rng.gen::()); + + if (center - l).length() > 0.9 { + if choose_material_probability < 0.8 { + // diffuse material + world.push(Box::new(Sphere::with_material( + center, + radius, + Box::new(Lambertian::new(Vec3::new( + rng.gen::() * rng.gen::(), + rng.gen::() * rng.gen::(), + rng.gen::() * rng.gen::(), + ))), + ))); + } else if choose_material_probability < 0.95 { + // metal material + world.push(Box::new(Sphere::with_material( + center, + radius, + Box::new(Metal::with_fuzz( + Vec3::new( + (1.0 + rng.gen::()) * 0.5, + (1.0 + rng.gen::()) * 0.5, + (1.0 + rng.gen::()) * 0.5, + ), + 0.5 * rng.gen::(), + )), + ))); + } else { + // glass material + world.push(Box::new(Sphere::with_material( + center, + radius, + Box::new(Dielectric::new(1.5)), + ))); + } + } + } + } + + world.push(Box::new(Sphere::with_material( + Vec3::new(0.0, 1.0, 0.0), + 1.0, + Box::new(Dielectric::new(1.5)), + ))); + world.push(Box::new(Sphere::with_material( + Vec3::new(-4.0, 1.0, 0.0), + 1.0, + Box::new(Lambertian::new(Vec3::new(0.4, 0.2, 0.1))), + ))); + world.push(Box::new(Sphere::with_material( + Vec3::new(4.0, 1.0, 0.0), + 1.0, + Box::new(Metal::with_fuzz(Vec3::new(0.7, 0.6, 0.5), 0.0)), + ))); + + println!("world size = {}", world.list.len()); + Some(world) + } + + fn camera(&self, aspect_ratio: f64) -> Option { + let lookfrom = Vec3::new(13.0, 2.0, 3.0); + let lookat = Vec3::new(0.0, 0.0, 0.0); + let camera = Camera::new( + lookfrom, + lookat, + Vec3::new(0.0, 1.0, 0.0), + 20.0, + aspect_ratio, + 0.1, + 10.0, + ); + Some(camera) + } + + fn render_chunk( + &self, + chunk: &mut Chunk, + camera: Option<&Camera>, + world: Option<&HitableList>, + samples: u8, + ) { + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; + let camera = camera.unwrap(); + let world = world.unwrap(); + + let mut rng = rand::thread_rng(); + 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); + 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 ray = camera.get_ray(u, v); + color += calc_color(ray, &world, 0); + } + + color /= samples as f64; + + // gamma 2 corrected + buffer[offset] = (255.99 * color.r().sqrt()) as u8; + buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8; + buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8; + offset += 4; + } + } + } +} + +fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> 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 + } else { + Vec3::new(0.0, 0.0, 0.0) + } + } + } else { + let unit_direction = ray.direction().unit_vector(); + let t = 0.5 * (unit_direction.y() + 1.0); + Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t + } +} diff --git a/src/demos/hitable_sphere.rs b/src/demos/hitable_sphere.rs index 3a883c6..cdbeaa8 100644 --- a/src/demos/hitable_sphere.rs +++ b/src/demos/hitable_sphere.rs @@ -26,13 +26,15 @@ impl Demo for HitableSphere { world: Option<&HitableList>, _samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; let world = world.unwrap(); let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0); diff --git a/src/demos/linear_gradient_rectangle.rs b/src/demos/linear_gradient_rectangle.rs index 7b6f437..892b62c 100644 --- a/src/demos/linear_gradient_rectangle.rs +++ b/src/demos/linear_gradient_rectangle.rs @@ -18,13 +18,15 @@ impl Demo for LinearGradientRectangle { _world: Option<&HitableList>, _samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; // -2.0 and 4.0 in lower_left_corner and horizontal respectively // because our canvas is in 2:1 ratio diff --git a/src/demos/materials.rs b/src/demos/materials.rs index f1b6a58..51d42f4 100644 --- a/src/demos/materials.rs +++ b/src/demos/materials.rs @@ -51,13 +51,15 @@ impl Demo for Materials { world: Option<&HitableList>, samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; let camera = camera.unwrap(); let mut rng = rand::thread_rng(); diff --git a/src/demos/mod.rs b/src/demos/mod.rs index 94a08f6..de4fb1a 100644 --- a/src/demos/mod.rs +++ b/src/demos/mod.rs @@ -1,6 +1,7 @@ mod defocus_blur; mod dielectric_material; mod diffuse_materials; +mod final_scene; mod hitable_sphere; mod linear_gradient_rectangle; mod materials; @@ -13,6 +14,7 @@ mod surface_normal_sphere; pub use defocus_blur::DefocusBlur; pub use dielectric_material::DielectricMaterial; pub use diffuse_materials::DiffuseMaterials; +pub use final_scene::FinalScene; pub use hitable_sphere::HitableSphere; pub use linear_gradient_rectangle::LinearGradientRectangle; pub use materials::Materials; @@ -35,7 +37,6 @@ use { }, }; -#[derive(Debug)] pub struct Chunk { x: usize, y: usize, diff --git a/src/demos/positionable_camera.rs b/src/demos/positionable_camera.rs index b9847fd..efa2ba8 100644 --- a/src/demos/positionable_camera.rs +++ b/src/demos/positionable_camera.rs @@ -66,13 +66,15 @@ impl Demo for PositionableCamera { world: Option<&HitableList>, samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; let camera = camera.unwrap(); let world = world.unwrap(); let mut rng = rand::thread_rng(); diff --git a/src/demos/simple_antialiasing.rs b/src/demos/simple_antialiasing.rs index 9b61bee..e694256 100644 --- a/src/demos/simple_antialiasing.rs +++ b/src/demos/simple_antialiasing.rs @@ -29,13 +29,15 @@ impl Demo for SimpleAntialiasing { world: Option<&HitableList>, samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; let camera = camera.unwrap(); let mut rng = rand::thread_rng(); diff --git a/src/demos/simple_rectangle.rs b/src/demos/simple_rectangle.rs index 0619504..07d591a 100644 --- a/src/demos/simple_rectangle.rs +++ b/src/demos/simple_rectangle.rs @@ -18,14 +18,15 @@ impl Demo for SimpleRectangle { _world: Option<&HitableList>, _samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; - + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; let mut offset = 0; for j in start_y..start_y + ny { diff --git a/src/demos/simple_sphere.rs b/src/demos/simple_sphere.rs index ec5fda4..9badcd7 100644 --- a/src/demos/simple_sphere.rs +++ b/src/demos/simple_sphere.rs @@ -20,14 +20,15 @@ impl Demo for SimpleSphere { _world: Option<&HitableList>, _samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; - + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; // Usually, lower_left_corner should've been -1.0,-1.0,-1.0 and // horizontal should've been 2.0,0.0,0.0 // but we are working with a canvas that is 2:1 in size. diff --git a/src/demos/surface_normal_sphere.rs b/src/demos/surface_normal_sphere.rs index c400e75..d804f0b 100644 --- a/src/demos/surface_normal_sphere.rs +++ b/src/demos/surface_normal_sphere.rs @@ -19,14 +19,15 @@ impl Demo for SurfaceNormalSphere { _world: Option<&HitableList>, _samples: u8, ) { - let x = chunk.x; - let y = chunk.y; - let nx = chunk.nx; - let ny = chunk.ny; - let start_x = chunk.start_x; - let start_y = chunk.start_y; - let buffer = &mut chunk.buffer; - + let &mut Chunk { + x, + y, + nx, + ny, + start_x, + start_y, + ref mut buffer, + } = chunk; // Usually, lower_left_corner should've been -1.0,-1.0,-1.0 and // horizontal should've been 2.0,0.0,0.0 // but we are working with a canvas that is 2:1 in size. diff --git a/src/main.rs b/src/main.rs index 0bedb11..52d32e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,8 +23,7 @@ const HORIZONTAL_PARTITION: usize = 8; fn main() -> Result<(), String> { let sdl_ctx = sdl2::init()?; let video_subsys = sdl_ctx.video()?; - - let (mut width, mut height) = (1280usize, 640usize); + 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) @@ -81,6 +80,7 @@ fn main() -> Result<(), String> { Some(Keycode::Num9) => active_demo = Box::new(demos::DielectricMaterial), Some(Keycode::Num0) => active_demo = Box::new(demos::PositionableCamera), Some(Keycode::Minus) => active_demo = Box::new(demos::DefocusBlur), + Some(Keycode::Equals) => active_demo = Box::new(demos::FinalScene), None => unreachable!(), _ => (), }; diff --git a/src/types/hitable_list.rs b/src/types/hitable_list.rs index b204992..f2fac57 100644 --- a/src/types/hitable_list.rs +++ b/src/types/hitable_list.rs @@ -17,3 +17,9 @@ impl Hitable for HitableList { hit_rec } } + +impl HitableList { + pub fn push(&mut self, obj: Box) { + self.list.push(obj); + } +}