From 6b5f2114225078413b25b8148aaa2a4e3bea4093 Mon Sep 17 00:00:00 2001 From: Soumya Date: Wed, 14 Aug 2024 21:04:23 +0200 Subject: [PATCH 1/3] [12.1] Camera Viewing Geometry - Added vertical field of view. This helps to zoom in or out of an image %SOFTWARE --- lib/src/utilities/camera.rs | 7 +++++-- src/main.rs | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/src/utilities/camera.rs b/lib/src/utilities/camera.rs index 462b4aa..62336e4 100644 --- a/lib/src/utilities/camera.rs +++ b/lib/src/utilities/camera.rs @@ -12,6 +12,7 @@ pub struct Camera { pub image_width: i32, pub samples_per_pixel: i32, pub max_depth: i32, // Maximum number of ray bounces + pub vertical_field_of_view: f64, image_height: i32, camera_center: Point3, pixel00_loc: Point3, // Location of pixel 0, 0 @@ -26,6 +27,7 @@ impl Camera { aspect_ratio: 1.0, image_width: 100, samples_per_pixel: 10, + vertical_field_of_view: 90.0, ..Default::default() // this is possible using the derive(Default) } } @@ -68,7 +70,9 @@ impl Camera { self.camera_center = Point3::new(0.0, 0.0, 0.0); // Camera - let viewport_height: f64 = 2.0; + let focal_length: f64 = 1.0; + let theta: f64 = self.vertical_field_of_view.to_radians(); + let viewport_height: f64 = 2.0 * focal_length * (theta / 2.0).tan(); let viewport_width: f64 = viewport_height * (self.image_width as f64 / self.image_height as f64); @@ -81,7 +85,6 @@ impl Camera { self.pixel_delta_v = viewport_v / (self.image_height as f64); // Calculate the location of the upper left pixel - let focal_length: f64 = 1.0; let viewport_origin: Point3 = self.camera_center - Vector3::new(0.0, 0.0, focal_length) - (viewport_u / 2.0) diff --git a/src/main.rs b/src/main.rs index e01eabe..05bba7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ const ASPECT_RATIO: f64 = 16.0 / 9.0; const IMAGE_WIDTH: i32 = 384; const SAMPLES_PER_PIXEL: i32 = 100; const MAX_DEPTH: i32 = 50; +const VERTICAL_FOV: f64 = 60.0; fn main() { // https://raytracing.github.io/books/RayTracingInOneWeekend.html @@ -20,7 +21,7 @@ fn main() { let material_ground = Box::new(Lambertian::new(Color::new(0.8, 0.8, 0.0))); let material_center = Box::new(Lambertian::new(Color::new(0.1, 0.2, 0.5))); let material_left = Box::new(Dielectric::new(1.33)); - let material_right = Box::new(Metal::new(Color::new(0.8, 0.6, 0.2), 1.0)); + let material_right = Box::new(Metal::new(Color::new(0.8, 0.6, 0.2), 0.7)); let material_bubble = Box::new(Dielectric::new(1.0 / 1.33)); world.push(Box::new(Sphere::new( @@ -55,5 +56,6 @@ fn main() { cam.image_width = IMAGE_WIDTH; cam.samples_per_pixel = SAMPLES_PER_PIXEL; cam.max_depth = MAX_DEPTH; + cam.vertical_field_of_view = VERTICAL_FOV; // Zooms in/out of the image cam.render(world); } From 8c5514f5c4efc535c1fa221f651f6013c57dbf0a Mon Sep 17 00:00:00 2001 From: Soumya Date: Wed, 14 Aug 2024 21:29:16 +0200 Subject: [PATCH 2/3] [12.2] Positioning and Orienting the Camera - Added camera look from and look at and an "up" vector %SOFTWARE --- lib/src/utilities/camera.rs | 40 +++++++++++++++++++++++++++++-------- src/main.rs | 6 ++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/lib/src/utilities/camera.rs b/lib/src/utilities/camera.rs index 62336e4..c1abcf4 100644 --- a/lib/src/utilities/camera.rs +++ b/lib/src/utilities/camera.rs @@ -1,11 +1,23 @@ use rand::Rng; use super::{ - color::Color, geometry::Hittable, interval::Interval, material::Scatter, point::Point3, - ray::Ray, vector3::Vector3, + color::Color, + geometry::Hittable, + interval::Interval, + material::Scatter, + point::Point3, + ray::Ray, + vector3::{Cross, Vector3}, }; use std::{fs::File, io::Write}; +#[derive(Default, Clone)] +pub struct CameraFrameBasis { + u: Vector3, + v: Vector3, + w: Vector3, +} + #[derive(Default, Clone)] pub struct Camera { pub aspect_ratio: f64, @@ -13,12 +25,16 @@ pub struct Camera { pub samples_per_pixel: i32, pub max_depth: i32, // Maximum number of ray bounces pub vertical_field_of_view: f64, + pub look_from: Point3, + pub look_at: Point3, + pub vertical_camera_up: Vector3, image_height: i32, camera_center: Point3, pixel00_loc: Point3, // Location of pixel 0, 0 pixel_delta_u: Vector3, // Offset to pixel to the right pixel_delta_v: Vector3, // Offset to pixel below pixel_samples_scale: f64, // Color scale factor for a sum of pixel samples + frame_basis: CameraFrameBasis, } impl Camera { @@ -28,6 +44,9 @@ impl Camera { image_width: 100, samples_per_pixel: 10, vertical_field_of_view: 90.0, + look_from: Point3::new(0.0, 0.0, 0.0), + look_at: Point3::new(0.0, 0.1, -1.0), + vertical_camera_up: Vector3::new(0.0, 1.0, 0.0), ..Default::default() // this is possible using the derive(Default) } } @@ -67,18 +86,23 @@ impl Camera { } self.pixel_samples_scale = 1.0 / self.samples_per_pixel as f64; - self.camera_center = Point3::new(0.0, 0.0, 0.0); + self.camera_center = self.look_from; - // Camera - let focal_length: f64 = 1.0; + // Camera - Viewport dimensions + let focal_length: f64 = (self.look_from - self.look_at).as_vec().length(); let theta: f64 = self.vertical_field_of_view.to_radians(); let viewport_height: f64 = 2.0 * focal_length * (theta / 2.0).tan(); let viewport_width: f64 = viewport_height * (self.image_width as f64 / self.image_height as f64); + // Calculate the basis vectors for the camera frame + self.frame_basis.w = (self.look_from - self.look_at).as_vec().unit_vector(); + self.frame_basis.u = (self.vertical_camera_up.cross_prod(self.frame_basis.w)).unit_vector(); + self.frame_basis.v = self.frame_basis.w.cross_prod(self.frame_basis.u); + // Calculate the vectors across the horizontal and down the vertical viewport edges - let viewport_u: Vector3 = Vector3::new(viewport_width, 0.0, 0.0); - let viewport_v: Vector3 = Vector3::new(0.0, -viewport_height, 0.0); + let viewport_u: Vector3 = self.frame_basis.u * viewport_width; // Vector across viewport horizontal edge + let viewport_v: Vector3 = -self.frame_basis.v * viewport_height; // Vector down viewport vertical edge // Calculate the horizontal and vertical delta vectors from pixel to pixel self.pixel_delta_u = viewport_u / (self.image_width as f64); @@ -86,7 +110,7 @@ impl Camera { // Calculate the location of the upper left pixel let viewport_origin: Point3 = self.camera_center - - Vector3::new(0.0, 0.0, focal_length) + - (self.frame_basis.w * focal_length) - (viewport_u / 2.0) - (viewport_v / 2.0); diff --git a/src/main.rs b/src/main.rs index 05bba7c..2e64d73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ use lib::utilities::{ geometry::{Hittable, Sphere}, material::{Dielectric, Lambertian, Metal}, point::Point3, + vector3::Vector3, }; const ASPECT_RATIO: f64 = 16.0 / 9.0; @@ -56,6 +57,11 @@ fn main() { cam.image_width = IMAGE_WIDTH; cam.samples_per_pixel = SAMPLES_PER_PIXEL; cam.max_depth = MAX_DEPTH; + cam.vertical_field_of_view = VERTICAL_FOV; // Zooms in/out of the image + cam.look_from = Point3::new(-2.0, 2.0, 1.0); + cam.look_at = Point3::new(0.0, 0.0, -1.0); + cam.vertical_camera_up = Vector3::new(0.0, 1.0, 0.0); + cam.render(world); } From b70e813efc81f10b4af5af0cc21143d09b27f353 Mon Sep 17 00:00:00 2001 From: Soumya Date: Thu, 15 Aug 2024 16:42:49 +0200 Subject: [PATCH 3/3] [14.1] A Final Render - Added random balls of random materials. %SOFTWARE --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 96 +++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 73 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb280ed..90cc804 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,7 @@ name = "ray_tracer" version = "0.1.0" dependencies = [ "lib", + "rand 0.3.23", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 257d96f..bb134b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ authors = ["Sen"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] lib ={ path = "lib"} # needed for the tests to work +rand="0.3.14" [[bin]] name = "bin" diff --git a/src/main.rs b/src/main.rs index 2e64d73..5504032 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use rand::Rng; + use lib::utilities::{ camera::Camera, color::Color, @@ -7,11 +9,13 @@ use lib::utilities::{ vector3::Vector3, }; +const NUMBER_BALLS: i32 = 5; + const ASPECT_RATIO: f64 = 16.0 / 9.0; -const IMAGE_WIDTH: i32 = 384; -const SAMPLES_PER_PIXEL: i32 = 100; +const IMAGE_WIDTH: i32 = 640; +const SAMPLES_PER_PIXEL: i32 = 500; const MAX_DEPTH: i32 = 50; -const VERTICAL_FOV: f64 = 60.0; +const VERTICAL_FOV: f64 = 40.0; fn main() { // https://raytracing.github.io/books/RayTracingInOneWeekend.html @@ -19,36 +23,78 @@ fn main() { // World let mut world: Vec> = Vec::new(); - let material_ground = Box::new(Lambertian::new(Color::new(0.8, 0.8, 0.0))); - let material_center = Box::new(Lambertian::new(Color::new(0.1, 0.2, 0.5))); - let material_left = Box::new(Dielectric::new(1.33)); - let material_right = Box::new(Metal::new(Color::new(0.8, 0.6, 0.2), 0.7)); - let material_bubble = Box::new(Dielectric::new(1.0 / 1.33)); - + // Scene - ground + let material_ground = Box::new(Lambertian::new(Color::new(0.8, 0.8, 0.8))); world.push(Box::new(Sphere::new( - Point3::new(0.0, -100.5, -1.0), - 100.0, + Point3::new(0.0, -1000.0, 0.0), + 1000.0, material_ground, ))); + + // Scene - small balls (random) + for x_index in -NUMBER_BALLS..NUMBER_BALLS { + for y_index in -NUMBER_BALLS..NUMBER_BALLS { + let choose_material_random: f64 = rand::thread_rng().r#gen::(); + let center = Point3::new( + (x_index as f64) + (0.9 * rand::thread_rng().r#gen::()), + 0.2, + (y_index as f64) + (0.9 * rand::thread_rng().r#gen::()), + ); + if choose_material_random < 0.6 { + // Lambertian + let material_lambertian = Box::new(Lambertian::new(Color::new( + rand::thread_rng().r#gen::(), + rand::thread_rng().r#gen::(), + rand::thread_rng().r#gen::(), + ))); + world.push(Box::new(Sphere::new(center, 0.2, material_lambertian))); + } else if choose_material_random < 0.85 { + // Metal + let material_metal = Box::new(Metal::new( + Color::new( + rand::thread_rng().r#gen::(), + rand::thread_rng().r#gen::(), + rand::thread_rng().r#gen::(), + ), + rand::thread_rng().r#gen::(), + )); + world.push(Box::new(Sphere::new(center, 0.2, material_metal))); + } else { + // Glass + let material_glass = Box::new(Dielectric::new(1.33)); + world.push(Box::new(Sphere::new(center, 0.2, material_glass))); + } + } + } + + // Scene - big balls with Glass material + let material_glass = Box::new(Dielectric::new(1.0 / 1.55)); world.push(Box::new(Sphere::new( - Point3::new(0.0, 0.0, -1.2), - 0.5, - material_center, + Point3::new(-8.0, 1.0, 0.0), + 1.0, + material_glass, ))); + let material_bubble = Box::new(Dielectric::new(1.55)); world.push(Box::new(Sphere::new( - Point3::new(-1.0, 0.0, -1.0), - 0.5, - material_left, + Point3::new(0.0, 1.0, 0.0), + 1.0, + material_bubble, ))); + + // Scene - big ball with Lambertian material + let material_lambertian = Box::new(Lambertian::new(Color::new(0.4, 0.2, 0.1))); world.push(Box::new(Sphere::new( - Point3::new(1.0, 0.0, -1.0), - 0.5, - material_right, + Point3::new(-4.0, 1.0, 0.0), + 1.0, + material_lambertian, ))); + + // Scene - big ball with Metal material + let material_metal = Box::new(Metal::new(Color::new(0.7, 0.6, 0.5), 0.2)); world.push(Box::new(Sphere::new( - Point3::new(-1.0, 0.0, -1.0), - 0.4, - material_bubble, + Point3::new(4.0, 1.0, 0.0), + 1.0, + material_metal, ))); // Camera @@ -59,8 +105,8 @@ fn main() { cam.max_depth = MAX_DEPTH; cam.vertical_field_of_view = VERTICAL_FOV; // Zooms in/out of the image - cam.look_from = Point3::new(-2.0, 2.0, 1.0); - cam.look_at = Point3::new(0.0, 0.0, -1.0); + cam.look_from = Point3::new(13.0, 2.0, 3.0); + cam.look_at = Point3::new(0.0, 0.0, 0.0); cam.vertical_camera_up = Vector3::new(0.0, 1.0, 0.0); cam.render(world);