From 4e67bbef8d4e918e87c8e68a07b699d2de1ebedf Mon Sep 17 00:00:00 2001 From: STP Date: Sat, 25 Nov 2023 21:15:46 -0500 Subject: [PATCH] Fixed primitives and working towards view matrix --- src/camera.rs | 155 +++------------------- src/primitive.rs | 332 +++++++++++++---------------------------------- src/ray.rs | 32 +++++ 3 files changed, 144 insertions(+), 375 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 4bbb4f8..d75fbae 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -11,163 +11,48 @@ pub struct Camera { eye: Point3, target: Point3, up: Vector3, - fovy: f64, - width: u32, - height: u32, - matrix: Matrix4, - inverse: Matrix4, - pub rays: Vec, + view: Matrix4, + inv_view: Matrix4, } #[allow(dead_code)] impl Camera { - pub fn new( - eye: Point3, - target: Point3, - up: Vector3, - width: u32, - height: u32, - fovy: f64, - ) -> Self { - let (matrix, inverse) = build_matrix_and_inverse(&eye, &target, &up, width, height, fovy); - let rays = cast_rays(&eye, &target, &up, width, height, fovy); + pub fn new(eye: Point3, target: Point3, up: Vector3) -> Self { + let view = Matrix4::look_at_lh(&eye, &target, &up); + let inv_view = view.try_inverse().unwrap(); Camera { eye, target, up, - width, - height, - fovy, - matrix, - inverse, - rays, + view, + inv_view, } } - pub fn new_sizeless( - eye: Point3, - target: Point3, - up: Vector3, - fovy: f64, - ) -> Self { - Camera::new(eye, target, up, 1, 1, fovy) - } - pub fn unit() -> Self { let eye = Point3::new(0.0, 0.0, 1.0); let target = Point3::new(0.0, 0.0, 0.0); let up = Vector3::new(0.0, 1.0, 0.0); - Camera::new(eye, target, up, 1, 1, 90.0) + Camera::new(eye, target, up) } - pub fn cast_rays(&self) -> Vec { - cast_rays( - &self.eye, - &self.target, - &self.up, - self.width, - self.height, - self.fovy, - ) + pub fn set_eye(&mut self, new_eye: Point3) { + self.eye = new_eye; + self.recalculate_matrix(); } - pub fn build_matrix_and_inverse(&self) -> (Matrix4, Matrix4) { - build_matrix_and_inverse( - &self.eye, - &self.target, - &self.up, - self.width, - self.height, - self.fovy, - ) + pub fn set_target(&mut self, new_target: Point3) { + self.target = new_target; + self.recalculate_matrix(); } - pub fn cast_ray(&self, x: u32, y: u32) -> Ray { - let aspect = self.width as f64 / self.height as f64; - let fovy_radians = (self.fovy as f64).to_radians(); - let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan(); - let view_direction = (self.target - self.eye).normalize(); - let hor = (view_direction.cross(&self.up)).normalize(); - let vert = (view_direction.cross(&hor)).normalize(); - let h_width = 2.0 * (fovh_radians / 2.0).tan(); - let v_height = 2.0 * (fovy_radians / 2.0).tan(); - //All good - let d_hor_vec = hor * (h_width / self.width as f64) as f64; - let d_vert_vec = vert * (v_height / self.height as f64) as f64; - - let half_w = self.width as i32 / 2; - let half_h = self.height as i32 / 2; - - let horizontal = (x as i32 - half_w) as f64 * (d_hor_vec); - let vertical = (-(y as i32) + half_h) as f64 * (d_vert_vec); - - let direction = view_direction + horizontal + vertical; - - Ray::new(self.eye, Unit::new_normalize(direction)) + pub fn set_up(&mut self, new_up: Vector3) { + self.up = new_up; + self.recalculate_matrix(); } - pub fn set_position(&mut self, eye: Point3) { - self.eye = eye; - } - - pub fn set_size(&mut self, width: u32, height: u32) { - self.width = width; - self.height = height; - self.rays = self.cast_rays(); - (self.matrix, self.inverse) = self.build_matrix_and_inverse(); + fn recalculate_matrix(&mut self) { + self.view = Matrix4::look_at_lh(&self.eye, &self.target, &self.up); + self.inv_view = self.view.try_inverse().unwrap(); } } - -fn build_matrix_and_inverse( - eye: &Point3, - target: &Point3, - up: &Vector3, - width: u32, - height: u32, - fovy: f64, -) -> (Matrix4, Matrix4) { - let view = Matrix4::look_at_lh(eye, target, up); - let aspect = width as f64 / height as f64; - let proj = Perspective3::new(aspect, fovy, ZNEAR, ZFAR); - let matrix = proj.as_matrix() * view; - let inverse = view.try_inverse().expect("No view") * proj.inverse(); - (matrix, inverse) -} - -fn cast_rays( - eye: &Point3, - target: &Point3, - up: &Vector3, - width: u32, - height: u32, - fovy: f64, -) -> Vec { - let aspect = width as f64 / height as f64; - let fovy_radians = (fovy as f64).to_radians(); - let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan(); - let view_direction = (target - eye).normalize(); - let hor = (view_direction.cross(&up)).normalize(); - let vert = (view_direction.cross(&hor)).normalize(); - let h_width = 2.0 * (fovh_radians / 2.0).tan() as f64; - let v_height = 2.0 * (fovy_radians / 2.0).tan() as f64; - //All good - let d_hor_vec = hor * (h_width / width as f64); - let d_vert_vec = vert * (v_height / height as f64); - - let mut rays = Vec::with_capacity(width as usize * height as usize); - - let half_w = width as i32 / 2; - let half_h = height as i32 / 2; - - for j in 0..height as i32 { - for i in 0..width as i32 { - let horizontal = (i - half_w) as f64 * (d_hor_vec); - let vertical = (-j + half_h) as f64 * (d_vert_vec); - - let direction = view_direction + horizontal + vertical; - let ray = Ray::new(eye.clone(), Unit::new_normalize(direction)); - rays.push(ray); - } - } - rays -} diff --git a/src/primitive.rs b/src/primitive.rs index 4bc28fc..0d27275 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -6,7 +6,6 @@ use roots::{find_roots_cubic, find_roots_quadratic, find_roots_quartic, Roots}; use std::fs::File; use std::io::{BufRead, BufReader}; use std::sync::Arc; - // MATERIAL ----------------------------------------------------------------- #[derive(Clone)] pub struct Material { @@ -14,6 +13,7 @@ pub struct Material { pub ks: Vector3, pub shininess: f64, } + impl Material { pub fn new(kd: Vector3, ks: Vector3, shininess: f64) -> Arc { Arc::new(Material { kd, ks, shininess }) @@ -49,6 +49,7 @@ impl Material { Arc::new(Material { kd, ks, shininess }) } } + // INTERSECTION ----------------------------------------------------------------- pub struct Intersection { // Information about an intersection @@ -58,6 +59,7 @@ pub struct Intersection { pub material: Arc, pub distance: f64, } + // BOUNDING BOX ----------------------------------------------------------------- #[derive(Clone)] struct BoundingBox { @@ -258,8 +260,7 @@ impl Primitive for Circle { #[derive(Clone)] pub struct Cylinder { radius: f64, - base: f64, - top: f64, + height: f64, base_circle: Arc, top_circle: Arc, material: Arc, @@ -267,25 +268,24 @@ pub struct Cylinder { } impl Cylinder { - pub fn new(radius: f64, base: f64, top: f64, material: Arc) -> Arc { + pub fn new(radius: f64, height: f64, material: Arc) -> Arc { let base_circle = Circle::new( - Point3::new(0.0, base, 0.0), + Point3::new(0.0, 0.0, 0.0), radius, Vector3::new(0.0, -1.0, 0.0), Arc::clone(&material), ); let top_circle = Circle::new( - Point3::new(0.0, top, 0.0), + Point3::new(0.0, height, 0.0), radius, Vector3::new(0.0, 1.0, 0.0), Arc::clone(&material), ); - let bln = Point3::new(-radius, base, -radius); - let trf = Point3::new(radius, top, radius); + let bln = Point3::new(-radius, 0.0, -radius); + let trf = Point3::new(radius, height, radius); Arc::new(Cylinder { radius, - base, - top, + height, base_circle, top_circle, material, @@ -325,7 +325,7 @@ impl Primitive for Cylinder { None => None, Some(t) => { let intersect = ray.at_t(t); - if intersect.y >= self.base && intersect.y <= self.top { + if intersect.y >= 0.0 && intersect.y <= self.height { let normal = Vector3::new(2.0 * intersect.x, 0.0, 2.0 * intersect.z); Some(Intersection { point: intersect, @@ -373,6 +373,7 @@ impl Primitive for Cylinder { _ => None, } } + fn get_material(&self) -> Arc { Arc::clone(&self.material) } @@ -585,38 +586,39 @@ impl Primitive for Rectangle { } } -// BOX ----------------------------------------------------------------- +// Cube ----------------------------------------------------------------- #[derive(Clone)] pub struct Cube { - width: f64, - height: f64, - depth: f64, + bln: Point3, + trf: Point3, material: Arc, bounding_box: BoundingBox, } impl Cube { - pub fn new(width: f64, height: f64, depth: f64, material: Arc) -> Arc { - let trf = Point3::new(width / 2.0, height / 2.0, depth / 2.0); - let bln = Point3::new(-width / 2.0, -height / 2.0, -depth / 2.0); + pub fn new(bln: Point3, trf: Point3, material: Arc) -> Arc { Arc::new(Cube { - width, - height, - depth, + bln, + trf, material, bounding_box: BoundingBox { bln, trf }, }) } + pub fn unit(material: Arc) -> Arc { - Cube::new(2.0, 2.0, 2.0, material) + let bln = Point3::new(-1.0, -1.0, -1.0); + let trf = Point3::new(1.0, 1.0, 1.0); + Cube::new(bln, trf, material) } } impl Primitive for Cube { fn intersect_ray(&self, ray: &Ray) -> Option { // Compute the minimum and maximum t-values for each axis of the bounding box - let t1 = (self.bounding_box.bln - ray.a).component_div(&ray.b); - let t2 = (self.bounding_box.trf - ray.a).component_div(&ray.b); + let bln = self.bln; + let trf = self.trf; + let t1 = (bln - ray.a).component_div(&ray.b); + let t2 = (trf - ray.a).component_div(&ray.b); // Find the largest minimum t-value and the smallest maximum t-value among the axes let tmin = t1.inf(&t2).max(); @@ -628,12 +630,12 @@ impl Primitive for Cube { let intersect = ray.at_t(tmin); // Check if the intersection is outside the box - if intersect.x < -self.width / 2.0 - || intersect.x > self.width / 2.0 - || intersect.y < -self.height / 2.0 - || intersect.y > self.height / 2.0 - || intersect.z < -self.depth / 2.0 - || intersect.z > self.depth / 2.0 + if intersect.x < bln.x + || intersect.x > trf.x + || intersect.y < bln.y + || intersect.y > trf.y + || intersect.z < bln.z + || intersect.z > trf.z { return None; // Intersection is outside the box } @@ -675,6 +677,7 @@ impl Primitive for Cube { } // TRIANGLE ----------------------------------------------------------------- +#[derive(Clone)] struct Triangle { u: Point3, v: Point3, @@ -765,6 +768,7 @@ impl Primitive for Triangle { } // MESH ----------------------------------------------------------------- +#[derive(Clone)] struct Mesh { triangles: Vec>, material: Arc, @@ -878,6 +882,7 @@ impl Primitive for Mesh { } } +// STEINER ----------------------------------------------------------------- #[derive(Clone)] pub struct SteinerSurface { material: Arc, @@ -986,6 +991,7 @@ fn smallest_non_zero(arr: &[f64]) -> Option { None } +// TORUS ----------------------------------------------------------------- #[derive(Clone)] pub struct Torus { inner_rad: f64, @@ -993,6 +999,7 @@ pub struct Torus { material: Arc, bounding_box: BoundingBox, } + impl Torus { pub fn new(inner_rad: f64, outer_rad: f64, material: Arc) -> Arc { // I need to find the bounding box for this shape @@ -1006,6 +1013,7 @@ impl Torus { }) } } + impl Primitive for Torus { fn intersect_ray(&self, ray: &Ray) -> Option { let a = ray.a.x; @@ -1118,235 +1126,79 @@ impl Primitive for Torus { } } +// GNOMON ----------------------------------------------------------------- #[derive(Clone)] -pub struct AdamShape { +pub struct Gnonom { + x_cube: Arc, + y_cube: Arc, + z_cube: Arc, material: Arc, bounding_box: BoundingBox, } -impl AdamShape { +impl Gnonom { + const GNONOM_WIDTH: f64 = 0.1; + const GNONOM_LENGTH: f64 = 2.0; pub fn new(material: Arc) -> Arc { - // I need to find the bounding box for this shape - let trf = Point3::new(1.0, 1.0, 1.0); - let bln = Point3::new(-1.0, -1.0, -1.0); - Arc::new(AdamShape { + let x_cube = Cube::new( + Point3::new(0.0, -Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH), + Point3::new(Self::GNONOM_LENGTH, Self::GNONOM_WIDTH, Self::GNONOM_WIDTH), + material.clone(), + ); + let y_cube = Cube::new( + Point3::new(-Self::GNONOM_WIDTH, 0.0, -Self::GNONOM_WIDTH), + Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_LENGTH, Self::GNONOM_WIDTH), + material.clone(), + ); + let z_cube = Cube::new( + Point3::new(-Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH, 0.0), + Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_WIDTH, Self::GNONOM_LENGTH), + material.clone(), + ); + let bounding_box = BoundingBox { + bln: Point3::new( + -Self::GNONOM_WIDTH, + -Self::GNONOM_WIDTH, + -Self::GNONOM_WIDTH, + ), + trf: Point3::new( + Self::GNONOM_LENGTH, + Self::GNONOM_LENGTH, + Self::GNONOM_LENGTH, + ), + }; + Arc::new(Gnonom { + x_cube, + y_cube, + z_cube, material, - bounding_box: BoundingBox { bln, trf }, + bounding_box, }) } } -impl Primitive for AdamShape { +impl Primitive for Gnonom { fn intersect_ray(&self, ray: &Ray) -> Option { - let a = ray.a.x; - let b = ray.b.x; - let c = ray.a.y; - let d = ray.b.y; - let e = ray.a.z; - let f = ray.b.z; - - let t0 = a.powf(3.0) + a * c * e + a * c; - let t1 = 3.0 * a.powf(2.0) * b + b * c * e + a * d * e + a * c * f + b * c + a * d; - let t2 = 3.0 * a * b.powf(2.0) + b * d * e + b * c * f + a * d * f + b * d; - let t3 = b.powf(3.0) + b * d * f; - - let t = match find_roots_cubic(t3, t2, t1, t0) { - Roots::No(arr) => smallest_non_zero(&arr), - Roots::One(arr) => smallest_non_zero(&arr), - Roots::Two(arr) => smallest_non_zero(&arr), - Roots::Three(arr) => smallest_non_zero(&arr), - Roots::Four(arr) => smallest_non_zero(&arr), + match self.x_cube.intersect_ray(ray) { + Some(intersect) => return Some(intersect), + None => (), }; - - let t = match t { - Some(t) => t, - None => return None, + match self.y_cube.intersect_ray(ray) { + Some(intersect) => return Some(intersect), + None => (), }; - - let point = ray.at_t(t); - let (x, y, z) = (point.x, point.y, point.z); - let dx = 3.0 * x.powf(2.0) + y * z + y; - let dy = x * z + x; - let dz = x * y; - let normal = Unit::new_normalize(Vector3::new(dx, dy, dz)); - - Some(Intersection { - point, - normal, - incidence: ray.b, - material: Arc::clone(&self.material), - distance: t, - }) + match self.z_cube.intersect_ray(ray) { + Some(intersect) => return Some(intersect), + None => (), + }; + None } + fn intersect_bounding_box(&self, ray: &Ray) -> Option> { self.bounding_box.intersect_bounding_box(ray) } fn get_material(&self) -> Arc { - Arc::clone(&self.material) - } -} -#[derive(Clone)] -pub struct AdamShape2 { - material: Arc, - bounding_box: BoundingBox, -} - -impl AdamShape2 { - pub fn new(material: Arc) -> Arc { - // I need to find the bounding box for this shape - let trf = Point3::new(1.0, 1.0, 1.0); - let bln = Point3::new(-1.0, -1.0, -1.0); - Arc::new(AdamShape2 { - material, - bounding_box: BoundingBox { bln, trf }, - }) - } -} - -impl Primitive for AdamShape2 { - fn intersect_ray(&self, ray: &Ray) -> Option { - let a = ray.a.x; - let b = ray.b.x; - let c = ray.a.y; - let d = ray.b.y; - let e = ray.a.z; - let f = ray.b.z; - - let t0 = a.powf(2.0) * c + a * c * e + a * c + c * e; - let t1 = 2.0 * a * b * c - + a.powf(2.0) * d - + b * c * e - + a * d * e - + a * c * f - + b * c - + a * d - + d * e - + c * f; - let t2 = - b.powf(2.0) * c + 2.0 * a * b * d + b * d * e + b * c * f + a * d * f + b * d + d * f; - let t3 = b.powf(2.0) * d + b * d * f; - - let t = match find_roots_cubic(t3, t2, t1, t0) { - Roots::No(arr) => smallest_non_zero(&arr), - Roots::One(arr) => smallest_non_zero(&arr), - Roots::Two(arr) => smallest_non_zero(&arr), - Roots::Three(arr) => smallest_non_zero(&arr), - Roots::Four(arr) => smallest_non_zero(&arr), - }; - - let t = match t { - Some(t) => t, - None => return None, - }; - - let point = ray.at_t(t); - let (x, y, z) = (point.x, point.y, point.z); - let dx = 2.0 * x * y + y * z + y.powf(2.0); - let dy = x.powf(2.0) + x * z + x + z; - let dz = x * y + y; - let normal = Unit::new_normalize(Vector3::new(dx, dy, dz)); - - Some(Intersection { - point, - normal, - incidence: ray.b, - material: Arc::clone(&self.material), - distance: t, - }) - } - fn intersect_bounding_box(&self, ray: &Ray) -> Option> { - self.bounding_box.intersect_bounding_box(ray) - } - - fn get_material(&self) -> Arc { - Arc::clone(&self.material) - } -} - -#[derive(Clone)] -pub struct AdamShape3 { - material: Arc, - bounding_box: BoundingBox, -} - -impl AdamShape3 { - pub fn new(material: Arc) -> Arc { - // I need to find the bounding box for this shape - let trf = Point3::new(1.0, 1.0, 1.0); - let bln = Point3::new(-1.0, -1.0, -1.0); - Arc::new(AdamShape3 { - material, - bounding_box: BoundingBox { bln, trf }, - }) - } -} - -impl Primitive for AdamShape3 { - fn intersect_ray(&self, ray: &Ray) -> Option { - let a = ray.a.x; - let b = ray.b.x; - let c = ray.a.y; - let d = ray.b.y; - let e = ray.a.z; - let f = ray.b.z; - - let t0 = a * c * e.powf(2.0) + a * c * e + a * e + c * e; - let t1 = b * c * e.powf(2.0) - + a * d * e.powf(2.0) - + 2.0 * a * c * e * f - + b * c * e - + a * d * e - + a * c * f - + b * e - + d * e - + a * f - + c * f; - let t2 = b * d * e.powf(2.0) - + 2.0 * b * c * e * f - + 2.0 * a * d * e * f - + a * c * f.powf(2.0) - + b * d * e - + b * c * f - + a * d * f - + b * f - + d * f; - let t3 = 2.0 * b * d * e * f + b * c * f.powf(2.0) + a * d * f.powf(2.0) + b * d * f; - let t4 = b * d * f.powf(2.0); - - let t = match find_roots_quartic(t4, t3, t2, t1, t0) { - Roots::No(arr) => smallest_non_zero(&arr), - Roots::One(arr) => smallest_non_zero(&arr), - Roots::Two(arr) => smallest_non_zero(&arr), - Roots::Three(arr) => smallest_non_zero(&arr), - Roots::Four(arr) => smallest_non_zero(&arr), - }; - - let t = match t { - Some(t) => t, - None => return None, - }; - - let point = ray.at_t(t); - let (x, y, z) = (point.x, point.y, point.z); - let dx = y * z.powf(2.0) + y * z + z; - let dy = x * z.powf(2.0) + x * z + z; - let dz = 2.0 * x * y * z + x * y + x + y; - let normal = Unit::new_normalize(Vector3::new(dx, dy, dz)); - - Some(Intersection { - point, - normal, - incidence: ray.b, - material: Arc::clone(&self.material), - distance: t, - }) - } - fn intersect_bounding_box(&self, ray: &Ray) -> Option> { - self.bounding_box.intersect_bounding_box(ray) - } - - fn get_material(&self) -> Arc { - Arc::clone(&self.material) + self.material.clone() } } diff --git a/src/ray.rs b/src/ray.rs index 0cfb4e6..0b50f54 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -40,6 +40,8 @@ impl Ray { for node in nodes { let primitive = node.primitive.clone(); + let trans = node.trans; + let inv_trans = node.inv_trans; if let Some(intersect) = primitive.intersect_ray(self) { if intersect.distance < closest_distance { @@ -51,4 +53,34 @@ impl Ray { closest_intersect } + + pub fn cast_rays(fovy: f64, width: u32, height: u32) -> Vec { + let aspect = width as f64 / height as f64; + let fovy_radians = fovy.to_radians(); + //Verify this part later + let dir = Vector3::new(0.0, 0.0, 1.0); + let up = Vector3::new(0.0, 1.0, 0.0); + let hor = Vector3::new(1.0, 0.0, 0.0); + let half_height = fovy_radians.tan(); + let half_width = aspect * half_height; + + let d_hor_vec = hor * (2.0 * half_width / width as f64) as f64; + let d_vert_vec = up * (2.0 * half_height / height as f64) as f64; + + //All good + + let mut rays = Vec::with_capacity(width as usize * height as usize); + + for j in 0..height as i32 { + for i in 0..width as i32 { + let horizontal = (i - half_width as i32) as f64 * (d_hor_vec); + let vertical = (-j + half_height as i32) as f64 * (d_vert_vec); + + let direction = dir + horizontal + vertical; + let ray = Ray::new(Point3::new(0.0, 0.0, 0.0), Unit::new_normalize(direction)); + rays.push(ray); + } + } + rays + } }