diff --git a/assignment-1c/.gitignore b/assignment-1c/.gitignore index 6987804..38a241f 100644 --- a/assignment-1c/.gitignore +++ b/assignment-1c/.gitignore @@ -1,6 +1,6 @@ /target -/assignment-1b -/raytracer1b +/assignment-1c +/raytracer1c /examples/*.png *.ppm *.zip diff --git a/assignment-1c/examples/smooth.txt b/assignment-1c/examples/smooth.txt index a93fc94..4c7e989 100644 --- a/assignment-1c/examples/smooth.txt +++ b/assignment-1c/examples/smooth.txt @@ -1,10 +1,10 @@ eye 0 0 4 viewdir 0 0 -1 -updir 0 1 0 +updir 1 1 0 hfov 60 imsize 640 480 -bkgcolor 0.1 0.1 0.1 +bkgcolor 0.4 0.4 0.4 light -2 1 0 1 1 1 1 mtlcolor 1 0.6 1 1 1 1 0.1 0.4 0.4 20 diff --git a/assignment-1c/raytracer1b b/assignment-1c/raytracer1b new file mode 100755 index 000000000..d653239 Binary files /dev/null and b/assignment-1c/raytracer1b differ diff --git a/assignment-1c/src/scene/illumination.rs b/assignment-1c/src/scene/illumination.rs index 0121016..c563612 100644 --- a/assignment-1c/src/scene/illumination.rs +++ b/assignment-1c/src/scene/illumination.rs @@ -205,12 +205,13 @@ impl Scene { }) .collect::>(); - let average = - intersections.iter().cloned().sum::() / intersections.len() as f64; - match intersections.is_empty() { true => 1.0, - false => average, + false => { + let average = intersections.iter().cloned().sum::() + / intersections.len() as f64; + average + } } } diff --git a/assignment-1c/src/scene/input_file.rs b/assignment-1c/src/scene/input_file.rs index 410d150..1b192cf 100644 --- a/assignment-1c/src/scene/input_file.rs +++ b/assignment-1c/src/scene/input_file.rs @@ -13,7 +13,7 @@ use crate::{ data::{Attenuation, Light, LightKind, Material}, object::{Object, ObjectKind}, sphere::Sphere, - texture::Texture, + texture::{Texture, NormalMap}, triangle::Triangle, Scene, }, @@ -260,6 +260,21 @@ impl Scene { scene.textures.push(texture); } + "bump" => { + let input_parent = path.parent().unwrap().to_path_buf(); + let path = match parts.next() { + Some(s) => input_parent.join(s), + None => bail!("Did not provide path."), + }; + + let idx = scene.textures.len(); + texture_idx = Some(idx); + + let image = Image::from_file(path)?; + let normal_map = NormalMap::new(image); + scene.normal_maps.push(normal_map); + } + _ => bail!("Unknown keyword {keyword}"), } } diff --git a/assignment-1c/src/scene/mod.rs b/assignment-1c/src/scene/mod.rs index 80bf7c8..e610ea1 100644 --- a/assignment-1c/src/scene/mod.rs +++ b/assignment-1c/src/scene/mod.rs @@ -12,7 +12,7 @@ use crate::{Point, Point2, Vector}; use self::data::{Attenuation, DepthCueing, Light, Material}; use self::object::Object; -use self::texture::Texture; +use self::texture::{Texture, NormalMap}; #[derive(Debug, Default)] pub struct Scene { @@ -39,6 +39,9 @@ pub struct Scene { /// List of textures pub textures: Vec, + /// List of normal maps (Extra credit) + pub normal_maps: Vec, + /// Coordinates into a texture image pub texture_vertices: Vec, diff --git a/assignment-1c/src/scene/object.rs b/assignment-1c/src/scene/object.rs index d453ae0..ffc83b7 100644 --- a/assignment-1c/src/scene/object.rs +++ b/assignment-1c/src/scene/object.rs @@ -45,8 +45,8 @@ impl ObjectKind { } } - /// Get the coordinates in the texture (between 0 and 1) that corresponds to - /// the intersection point + /// Get the (u, v) coordinates in the texture (between 0 and 1) that + /// corresponds to the intersection point pub fn get_texture_coord( &self, scene: &Scene, diff --git a/assignment-1c/src/scene/texture.rs b/assignment-1c/src/scene/texture.rs index 45aabfb..b94bfb9 100644 --- a/assignment-1c/src/scene/texture.rs +++ b/assignment-1c/src/scene/texture.rs @@ -1,4 +1,7 @@ -use crate::image::{Color, Image}; +use crate::{ + image::{Color, Image}, + Vector, +}; #[derive(Debug)] pub struct Texture(Image); @@ -39,3 +42,23 @@ impl Texture { + (alpha) * (beta) * self.pixel_at_exact(i + 1, j + 1) } } + +#[derive(Debug)] +pub struct NormalMap(Image); + +impl NormalMap { + pub fn new(image: Image) -> Self { + Self(image) + } + + pub fn normal_vector_at_exact(&self, x: usize, y: usize) -> Vector { + let vec = self.0.data[y * self.0.width + x]; + + // So, according to the instructions, this should actually be a value + // between -1 and 1. However, we're reading this in through an image. + // I'm just going to do the lazy thing here (which theoretically + // actually saves cycles) by only doing the transformation when loading + // out of the image + vec.map(|value| 2.0 * value / 255.0 - 1.0) + } +} diff --git a/assignment-1c/src/scene/triangle.rs b/assignment-1c/src/scene/triangle.rs index 8da1c2f..b5c4c96 100644 --- a/assignment-1c/src/scene/triangle.rs +++ b/assignment-1c/src/scene/triangle.rs @@ -1,3 +1,5 @@ +use std::f64::EPSILON; + use anyhow::Result; use nalgebra::{Matrix2, Vector2, Vector3}; use ordered_float::NotNan; @@ -73,7 +75,10 @@ impl Triangle { self.compute_barycentric_coordinates(scene, p)?; // Each of alpha, beta, and gamma must be between 0 and 1 - if ![alpha, beta, gamma].into_iter().all(|v| 0.0 < v && v < 1.0) { + if ![alpha, beta, gamma] + .into_iter() + .all(|v| 0.0 - EPSILON <= v && v <= 1.0 + EPSILON) + { return Ok(None); } @@ -87,6 +92,7 @@ impl Triangle { (alpha * n0 + beta * n1 + gamma * n2).normalize() } + None => n.normalize(), }; @@ -142,7 +148,7 @@ impl Triangle { (p0, p1, p2) } - /// Get the new basis vectors using p0 as the origin. + /// Get the new basis vectors using p0 as the origin. Returns (p0, e1, e2) #[inline] fn basis_vectors(&self, scene: &Scene) -> (Vector, Vector, Vector) { let (p0, p1, p2) = self.corner_coordinates(scene);