From 1d42ae9850ea036a1ae0e259065da84d5bf4a3a3 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Wed, 1 Feb 2023 02:23:45 -0600 Subject: [PATCH] Fix radians bug, polish and refactor --- assignment-1/Cargo.lock | 10 ----- assignment-1/Cargo.toml | 1 - assignment-1/src/main.rs | 69 +++++++++++----------------------- assignment-1/src/scene_data.rs | 47 ++++++++++++++++++++++- assignment-1/src/vec3.rs | 2 - assignment-1/src/view.rs | 11 ------ 6 files changed, 67 insertions(+), 73 deletions(-) delete mode 100644 assignment-1/src/view.rs diff --git a/assignment-1/Cargo.lock b/assignment-1/Cargo.lock index 012c9ed..f794792 100644 --- a/assignment-1/Cargo.lock +++ b/assignment-1/Cargo.lock @@ -14,7 +14,6 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "itertools", "num", "ordered-float", "rayon", @@ -188,15 +187,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "libc" version = "0.2.139" diff --git a/assignment-1/Cargo.toml b/assignment-1/Cargo.toml index f3d800b..bd448d6 100644 --- a/assignment-1/Cargo.toml +++ b/assignment-1/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [dependencies] anyhow = "1.0.68" clap = { version = "4.1.4", features = ["derive"] } -itertools = "0.10.5" num = { version = "0.4.0", features = ["serde"] } ordered-float = "3.4.0" rayon = "1.6.1" diff --git a/assignment-1/src/main.rs b/assignment-1/src/main.rs index a3f3a3e..ad58d68 100644 --- a/assignment-1/src/main.rs +++ b/assignment-1/src/main.rs @@ -6,14 +6,12 @@ mod input_file; mod ray; mod scene_data; mod vec3; -mod view; use std::fs::File; use std::path::PathBuf; use anyhow::Result; use clap::Parser; -use itertools::Itertools; use ordered_float::NotNan; use rayon::prelude::{IntoParallelIterator, ParallelIterator}; @@ -21,28 +19,21 @@ use crate::image::Image; use crate::input_file::parse_input_file; use crate::ray::Ray; use crate::scene_data::Object; -use crate::vec3::Vec3; -use crate::view::Rect; /// Viewing distance -const ARBITRARY_D: f64 = 2.0; +const ARBITRARY_D: f64 = 1.0; /// Simple raycaster. #[derive(Parser)] #[clap(author, version, about, long_about = None)] struct Opt { /// Path to the input file to use. - /// - /// The input file should follow this format: - /// - /// imsize [width] [height] - /// - /// Where `imsize' is a keyword, and `width' and `height' are integer values - /// denoting the desired size of the image to be generated. #[clap()] input_path: PathBuf, - #[clap()] + /// Path to the output (defaults to the same file name as the input except + /// with an extension of .ppm) + #[clap(short = 'o', long = "output")] output_path: Option, } @@ -53,38 +44,9 @@ fn main() -> Result<()> { .unwrap_or_else(|| opt.input_path.with_extension("ppm")); let scene = parse_input_file(&opt.input_path)?; - println!("Scene: {scene:?}"); - // Compute viewing directions - let u = Vec3::cross(scene.view_dir, scene.up_dir).unit(); - let v = Vec3::cross(u, scene.view_dir).unit(); - - // Compute dimensions of viewing window based on field of view - let viewing_width = { - // Divide the angle in 2 since we are trying to use trig rules so we must - // get it from a right triangle - let half_hfov = scene.hfov / 2.0; - - // tan(hfov / 2) = w / 2d - let w_over_2d = half_hfov.tan(); - - // To find the viewing width we must multiply by 2d now - w_over_2d * 2.0 * ARBITRARY_D - }; - let aspect_ratio = scene.image_width as f64 / scene.image_height as f64; - let viewing_height = viewing_width / aspect_ratio; - - // Compute viewing window corners - let n = scene.view_dir.unit(); - #[rustfmt::skip] // Otherwise this line wraps over - let view_window = Rect { - upper_left: scene.eye_pos + n * ARBITRARY_D - u * (viewing_width / 2.0) + v * (viewing_height / 2.0), - upper_right: scene.eye_pos + n * ARBITRARY_D + u * (viewing_width / 2.0) + v * (viewing_height / 2.0), - lower_left: scene.eye_pos + n * ARBITRARY_D - u * (viewing_width / 2.0) - v * (viewing_height / 2.0), - lower_right: scene.eye_pos + n * ARBITRARY_D + u * (viewing_width / 2.0) - v * (viewing_height / 2.0), - }; - - println!("Coords: {view_window:?}"); + // Compute the viewing window + let view_window = scene.compute_viewing_window(); // Translate image pixels to real-world 3d coords let translate_pixel = { @@ -93,7 +55,6 @@ fn main() -> Result<()> { let dy = view_window.lower_left - view_window.upper_left; let pixel_base_y = dy / scene.image_height as f64; - println!("Base components in 3D space: {pixel_base_x} / {pixel_base_y}"); move |px: usize, py: usize| { let x_component = pixel_base_x * px as f64; @@ -102,9 +63,11 @@ fn main() -> Result<()> { } }; + // Generate a parallel iterator for pixels + // The iterator preserves order and uses row-major order let pixels_iter = (0..scene.image_height) .into_par_iter() - .flat_map(|x| (0..scene.image_width).into_par_iter().map(move |y| (y, x))); + .flat_map(|y| (0..scene.image_width).into_par_iter().map(move |x| (x, y))); // Loop through every single pixel of the output file let pixels = pixels_iter @@ -122,9 +85,19 @@ fn main() -> Result<()> { * intersection as well */ }; - ray.intersects_at(sphere).map(|t| (t, sphere)) + // Return both the t and the sphere, because we want to sort on the t + // but later retrieve attributes from the sphere + ray.intersects_at(sphere).and_then(|t| { + // Unfortunately, IEEE floats in Rust don't have total ordering, + // because NaNs violate ordering properties. The way to remedy this + // is to ensure we don't have NaNs by wrapping it into this type, + // which then implements total ordering + let t = NotNan::new(t); + t.ok().map(|t| (t, sphere)) + }) }) - .min_by_key(|(t, _)| NotNan::new(*t).unwrap()); + // Sort the list of intersection times by the lowest one. + .min_by_key(|(t, _)| *t); let pixel_color = match earliest_intersection { Some((_, sphere)) => scene.material_colors[sphere.material], diff --git a/assignment-1/src/scene_data.rs b/assignment-1/src/scene_data.rs index 70b6baa..891765b 100644 --- a/assignment-1/src/scene_data.rs +++ b/assignment-1/src/scene_data.rs @@ -1,4 +1,4 @@ -use crate::{image::Color, vec3::Vec3}; +use crate::{image::Color, vec3::Vec3, ARBITRARY_D}; #[derive(Debug)] pub struct Sphere { @@ -20,6 +20,14 @@ pub enum Object { }, } +#[derive(Debug)] +pub struct Rect { + pub upper_left: Vec3, + pub upper_right: Vec3, + pub lower_left: Vec3, + pub lower_right: Vec3, +} + #[derive(Debug, Default)] pub struct Scene { pub eye_pos: Vec3, @@ -38,3 +46,40 @@ pub struct Scene { pub material_colors: Vec, pub objects: Vec, } + +impl Scene { + /// Determine the boundaries of the viewing window in world coordinates + pub fn compute_viewing_window(&self) -> Rect { + // Compute viewing directions + let u = Vec3::cross(self.view_dir, self.up_dir).unit(); + let v = Vec3::cross(u, self.view_dir).unit(); + + // Compute dimensions of viewing window based on field of view + let viewing_width = { + // Divide the angle in 2 since we are trying to use trig rules so we must + // get it from a right triangle + let half_hfov = self.hfov.to_radians() / 2.0; + + // tan(hfov / 2) = w / 2d + let w_over_2d = half_hfov.tan(); + + // To find the viewing width we must multiply by 2d now + w_over_2d * 2.0 * ARBITRARY_D + }; + + let aspect_ratio = self.image_width as f64 / self.image_height as f64; + let viewing_height = viewing_width / aspect_ratio; + + // Compute viewing window corners + let n = self.view_dir.unit(); + #[rustfmt::skip] // Otherwise this line wraps over + let view_window = Rect { + upper_left: self.eye_pos + n * ARBITRARY_D - u * (viewing_width / 2.0) + v * (viewing_height / 2.0), + upper_right: self.eye_pos + n * ARBITRARY_D + u * (viewing_width / 2.0) + v * (viewing_height / 2.0), + lower_left: self.eye_pos + n * ARBITRARY_D - u * (viewing_width / 2.0) - v * (viewing_height / 2.0), + lower_right: self.eye_pos + n * ARBITRARY_D + u * (viewing_width / 2.0) - v * (viewing_height / 2.0), + }; + + view_window + } +} diff --git a/assignment-1/src/vec3.rs b/assignment-1/src/vec3.rs index 88b5840..b2adfe0 100644 --- a/assignment-1/src/vec3.rs +++ b/assignment-1/src/vec3.rs @@ -3,8 +3,6 @@ use std::ops::{Add, Div, Mul, Sub}; use num::Float; -use crate::image::Color; - #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct Vec3 { pub x: T, diff --git a/assignment-1/src/view.rs b/assignment-1/src/view.rs deleted file mode 100644 index ea4ad38..000000000 --- a/assignment-1/src/view.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::vec3::Vec3; - -#[derive(Debug)] -pub struct Rect { - pub upper_left: Vec3, - pub upper_right: Vec3, - pub lower_left: Vec3, - pub lower_right: Vec3, -} - -pub fn compute_viewing_rect() {}