From bd06d32b34c9212fc117cc4f85417f6a4385a9ed Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Sat, 25 Feb 2023 16:20:59 -0600 Subject: [PATCH] Draw earth texture to the sphere --- assignment-1c/src/image.rs | 4 +++- assignment-1c/src/main.rs | 2 +- assignment-1c/src/scene/illumination.rs | 22 +++++++++++------- assignment-1c/src/scene/input_file.rs | 8 ++++--- assignment-1c/src/scene/mod.rs | 5 ++-- assignment-1c/src/scene/object.rs | 13 +++++++++++ assignment-1c/src/scene/sphere.rs | 19 +++++++++++++++ assignment-1c/src/scene/texture.rs | 31 ++++++++++++++++++++++--- 8 files changed, 85 insertions(+), 19 deletions(-) diff --git a/assignment-1c/src/image.rs b/assignment-1c/src/image.rs index 8ab73e4..c9d09ef 100644 --- a/assignment-1c/src/image.rs +++ b/assignment-1c/src/image.rs @@ -29,7 +29,9 @@ pub struct Image { impl Image { pub fn from_file(path: impl AsRef) -> Result { - let file = File::open(path.as_ref())?; + let path = path.as_ref(); + let file = File::open(path) + .with_context(|| format!("Could not open file at {path:?}"))?; Self::read(file) } diff --git a/assignment-1c/src/main.rs b/assignment-1c/src/main.rs index 8743832..91fe7e4 100644 --- a/assignment-1c/src/main.rs +++ b/assignment-1c/src/main.rs @@ -113,7 +113,7 @@ fn main() -> Result<()> { Ok(match earliest_intersection { // Take the object's material color Some((obj_idx, intersection_context, object)) => { - scene.compute_pixel_color(obj_idx, object, intersection_context) + scene.compute_pixel_color(obj_idx, object, intersection_context)? } // There was no intersection, so this should default to the scene's diff --git a/assignment-1c/src/scene/illumination.rs b/assignment-1c/src/scene/illumination.rs index bd067a9..6f90da8 100644 --- a/assignment-1c/src/scene/illumination.rs +++ b/assignment-1c/src/scene/illumination.rs @@ -1,5 +1,6 @@ use std::iter; +use anyhow::Result; use ordered_float::NotNan; use rand::Rng; use rayon::prelude::{ @@ -10,7 +11,8 @@ use rayon::prelude::{ use crate::{image::Color, ray::Ray, utils::dot, Point, Vector}; use super::{ - data::{DepthCueing, Light, LightKind, Object}, + data::{DepthCueing, Light, LightKind}, + object::Object, Scene, }; @@ -27,20 +29,22 @@ impl Scene { obj_idx: usize, object: &Object, intersection_context: IntersectionContext, - ) -> Color { - // TODO: Does it make sense to make this function fallible from an API - // design standpoint? + ) -> Result { let material = match self.materials.get(object.material_idx) { Some(v) => v, - None => return self.bkg_color, + None => bail!("Material index not found."), }; let diffuse_color = match object.texture_idx { Some(texture_idx) => { - let texture = &self.textures[texture_idx]; + let (u, v) = object.kind.get_texture_coord(&intersection_context)?; - // TODO: need to implement - material.diffuse_color + let texture = match self.textures.get(texture_idx) { + Some(v) => v, + None => bail!("Texture index not found."), + }; + + texture.pixel_at(u, v) } None => material.diffuse_color, }; @@ -132,7 +136,7 @@ impl Scene { // Need to clamp the result so none of the components goes over 1 let clamped_result = color.map(|v| v.min(1.0)); - clamped_result + Ok(clamped_result) } /// Perform another ray casting to see if there are any objects obstructing diff --git a/assignment-1c/src/scene/input_file.rs b/assignment-1c/src/scene/input_file.rs index 5f37fc3..1dee08a 100644 --- a/assignment-1c/src/scene/input_file.rs +++ b/assignment-1c/src/scene/input_file.rs @@ -10,15 +10,17 @@ use crate::{ image::{Color, Image}, scene::{ cylinder::Cylinder, - data::{Attenuation, Light, LightKind, Material, Object}, + data::{Attenuation, Light, LightKind, Material}, + object::{Object, ObjectKind}, sphere::Sphere, + texture::Texture, triangle::Triangle, - Scene, texture::Texture, + Scene, }, Point, Vector, }; -use super::data::{DepthCueing, ObjectKind}; +use super::data::DepthCueing; impl Scene { /// Parse the input file into a scene diff --git a/assignment-1c/src/scene/mod.rs b/assignment-1c/src/scene/mod.rs index 19c7e15..23ad075 100644 --- a/assignment-1c/src/scene/mod.rs +++ b/assignment-1c/src/scene/mod.rs @@ -2,15 +2,16 @@ pub mod cylinder; pub mod data; pub mod illumination; pub mod input_file; +pub mod object; pub mod sphere; pub mod texture; pub mod triangle; -pub mod object; use crate::image::{Color, Image}; use crate::{Point, Point2, Vector}; -use self::data::{Attenuation, DepthCueing, Light, Material, Object}; +use self::data::{Attenuation, DepthCueing, Light, Material}; +use self::object::Object; use self::texture::Texture; #[derive(Debug, Default)] diff --git a/assignment-1c/src/scene/object.rs b/assignment-1c/src/scene/object.rs index 6a8ca89..e8b6fff 100644 --- a/assignment-1c/src/scene/object.rs +++ b/assignment-1c/src/scene/object.rs @@ -44,4 +44,17 @@ impl ObjectKind { ObjectKind::Triangle(triangle) => triangle.intersects_ray_at(scene, ray), } } + + /// Get the coordinates in the texture (between 0 and 1) that corresponds to + /// the intersection point + pub fn get_texture_coord( + &self, + ctx: &IntersectionContext, + ) -> Result<(f64, f64)> { + match self { + ObjectKind::Sphere(sphere) => sphere.get_texture_coord(ctx), + ObjectKind::Cylinder(cylinder) => todo!(), + ObjectKind::Triangle(triangle) => todo!(), + } + } } diff --git a/assignment-1c/src/scene/sphere.rs b/assignment-1c/src/scene/sphere.rs index 283ff73..81f25a4 100644 --- a/assignment-1c/src/scene/sphere.rs +++ b/assignment-1c/src/scene/sphere.rs @@ -1,3 +1,5 @@ +use std::f64::consts::PI; + use anyhow::Result; use ordered_float::NotNan; @@ -72,4 +74,21 @@ impl Sphere { normal, })) } + + pub fn get_texture_coord( + &self, + ctx: &IntersectionContext, + ) -> Result<(f64, f64)> { + // Reverse engineer the angles from the coordinate of the intersection + let cosp = (ctx.point.z - self.center.z) / self.radius; + let phi = cosp.acos(); + let theta = + (ctx.point.y - self.center.y).atan2(ctx.point.x - self.center.x); + + // Map theta and phi into 0 - 1 range + let v = phi / PI; + let u = 0.5 + theta / (2.0 * PI); + + Ok((u, v)) + } } diff --git a/assignment-1c/src/scene/texture.rs b/assignment-1c/src/scene/texture.rs index fc94699..45aabfb 100644 --- a/assignment-1c/src/scene/texture.rs +++ b/assignment-1c/src/scene/texture.rs @@ -8,9 +8,34 @@ impl Texture { Self(image) } + pub fn pixel_at_exact(&self, x: usize, y: usize) -> Color { + // TODO: Debug asserts? + + self.0.data[y * self.0.width + x] + } + /// Returns a pixel at the given coordinate. For non-lattice coordinates, - /// interpolation of the image is done. - pub fn pixel_at(&self, x: f64, y: f64) -> Option { - todo!() + /// bi-linear interpolation of the image is done. + pub fn pixel_at(&self, u: f64, v: f64) -> Color { + debug_assert!(0.0 <= u && u <= 1.0, "u must be between 0 and 1"); + debug_assert!(0.0 <= v && v <= 1.0, "u must be between 0 and 1"); + + // Slide 121 + let x = u * (self.0.width - 1) as f64; + let y = v * (self.0.height - 1) as f64; + + let i = x.floor(); + let j = y.floor(); + + let alpha = x - i; + let beta = y - j; + + let i = i as usize; + let j = j as usize; + + (1.0 - alpha) * (1.0 - beta) * self.pixel_at_exact(i, j) + + (alpha) * (1.0 - beta) * self.pixel_at_exact(i + 1, j) + + (1.0 - alpha) * (beta) * self.pixel_at_exact(i, j + 1) + + (alpha) * (beta) * self.pixel_at_exact(i + 1, j + 1) } }