From a52863fe6ca3f9979bb7723260763a1dc80313f4 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Sat, 25 Feb 2023 17:16:36 -0600 Subject: [PATCH] Implement textured coordinates --- assignment-1c/grading-criteria.md | 2 +- assignment-1c/src/scene/illumination.rs | 4 +- assignment-1c/src/scene/object.rs | 3 +- assignment-1c/src/scene/triangle.rs | 102 +++++++++++++++++++----- 4 files changed, 90 insertions(+), 21 deletions(-) diff --git a/assignment-1c/grading-criteria.md b/assignment-1c/grading-criteria.md index 046cd28..85b545c 100644 --- a/assignment-1c/grading-criteria.md +++ b/assignment-1c/grading-criteria.md @@ -34,7 +34,7 @@ Due: Friday March 3rd color in the Phong illumination model when it is evaluated at that point. (25 pts) -- [ ] The program is capable of rendering textured triangles. The texture +- [x] The program is capable of rendering textured triangles. The texture coordinate at the ray/triangle intersection point is correctly interpolated from the texture coordinates defined at each of the three triangle vertices. The interpolated texture coordinate is used to retrieve diff --git a/assignment-1c/src/scene/illumination.rs b/assignment-1c/src/scene/illumination.rs index 6f90da8..0121016 100644 --- a/assignment-1c/src/scene/illumination.rs +++ b/assignment-1c/src/scene/illumination.rs @@ -37,7 +37,9 @@ impl Scene { let diffuse_color = match object.texture_idx { Some(texture_idx) => { - let (u, v) = object.kind.get_texture_coord(&intersection_context)?; + let (u, v) = object + .kind + .get_texture_coord(&self, &intersection_context)?; let texture = match self.textures.get(texture_idx) { Some(v) => v, diff --git a/assignment-1c/src/scene/object.rs b/assignment-1c/src/scene/object.rs index e8b6fff..d453ae0 100644 --- a/assignment-1c/src/scene/object.rs +++ b/assignment-1c/src/scene/object.rs @@ -49,12 +49,13 @@ impl ObjectKind { /// the intersection point pub fn get_texture_coord( &self, + scene: &Scene, ctx: &IntersectionContext, ) -> Result<(f64, f64)> { match self { ObjectKind::Sphere(sphere) => sphere.get_texture_coord(ctx), ObjectKind::Cylinder(cylinder) => todo!(), - ObjectKind::Triangle(triangle) => todo!(), + ObjectKind::Triangle(triangle) => triangle.get_texture_coord(scene, ctx), } } } diff --git a/assignment-1c/src/scene/triangle.rs b/assignment-1c/src/scene/triangle.rs index 690e04b..d7eecc5 100644 --- a/assignment-1c/src/scene/triangle.rs +++ b/assignment-1c/src/scene/triangle.rs @@ -4,6 +4,7 @@ use ordered_float::NotNan; use crate::ray::Ray; use crate::utils::{cross, dot}; +use crate::{Point, Vector}; use super::illumination::IntersectionContext; use super::Scene; @@ -11,7 +12,11 @@ use super::Scene; #[derive(Debug)] pub struct Triangle { pub vertices: Vector3, + + /// Indexes into the scene's normal coordinates list pub normals: Option>, + + /// Indexes into the scene's texture coordinates list pub textures: Option>, } @@ -21,17 +26,13 @@ impl Triangle { scene: &Scene, ray: &Ray, ) -> Result> { - let p0 = scene.triangle_vertices[self.vertices.x]; - let p1 = scene.triangle_vertices[self.vertices.y]; - let p2 = scene.triangle_vertices[self.vertices.z]; + let (p0, e1, e2) = self.basis_vectors(scene); // Solve for the plane equation coefficients A, B, C, D such that: // // $$ // Ax + By + Cz + D = 0 // $$ - let e1 = p1 - p0; - let e2 = p2 - p0; let n = cross(e1, e2); let a = n.x; let b = n.y; @@ -63,21 +64,10 @@ impl Triangle { // p = p0 + beta * e1 + gamma * e2 // Using the whack linear algebra approach derived on slide 57 let ep = point - p0; - let d = Matrix2::new(dot(e1, e1), dot(e1, e2), dot(e2, e1), dot(e2, e2)); let p = Vector2::new(dot(e1, ep), dot(e2, ep)); - let d_inv = match d.try_inverse() { - Some(v) => v, - // TODO: Whack - None => return Ok(None), - }; - - let sol = d_inv * p; - let beta = sol.x; - let gamma = sol.y; - - // Slide 46 - let alpha = 1.0 - beta - gamma; + let (alpha, beta, gamma) = + 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) { @@ -103,4 +93,80 @@ impl Triangle { normal, })) } + + pub fn get_texture_coord( + &self, + scene: &Scene, + ctx: &IntersectionContext, + ) -> Result<(f64, f64)> { + let texture_coordinates = match self.textures { + Some(v) => v, + None => { + bail!("Textured triangle requested without providing coordinates") + } + }; + + let p0 = scene.texture_vertices[texture_coordinates.x]; + let p1 = scene.texture_vertices[texture_coordinates.y]; + let p2 = scene.texture_vertices[texture_coordinates.z]; + + let p = self.convert_point(scene, ctx.point); + let (alpha, beta, gamma) = + self.compute_barycentric_coordinates(scene, p)?; + + let u = alpha * p0.x + beta * p1.x + gamma * p2.x; + let v = alpha * p0.y + beta * p1.y + gamma * p2.y; + + Ok((u, v)) + } + + fn convert_point(&self, scene: &Scene, point: Point) -> Vector2 { + let (p0, e1, e2) = self.basis_vectors(scene); + + let ep = point - p0; + Vector2::new(dot(e1, ep), dot(e2, ep)) + } + + /// Fetch the corners of the triangles from the scene + #[inline] + fn corner_coordinates(&self, scene: &Scene) -> (Point, Point, Point) { + let p0 = scene.triangle_vertices[self.vertices.x]; + let p1 = scene.triangle_vertices[self.vertices.y]; + let p2 = scene.triangle_vertices[self.vertices.z]; + + (p0, p1, p2) + } + + /// Get the new basis vectors using p0 as the origin. + #[inline] + fn basis_vectors(&self, scene: &Scene) -> (Vector, Vector, Vector) { + let (p0, p1, p2) = self.corner_coordinates(scene); + let e1 = p1 - p0; + let e2 = p2 - p0; + (p0, e1, e2) + } + + fn compute_barycentric_coordinates( + &self, + scene: &Scene, + p: Vector2, + ) -> Result<(f64, f64, f64)> { + let (_, e1, e2) = self.basis_vectors(scene); + let d = Matrix2::new(dot(e1, e1), dot(e1, e2), dot(e2, e1), dot(e2, e2)); + + let d_inv = match d.try_inverse() { + Some(v) => v, + // TODO: Whack + None => bail!("No inverse..."), + }; + + let sol = d_inv * p; + let beta = sol.x; + let gamma = sol.y; + + // Slide 46 + let alpha = 1.0 - beta - gamma; + + Ok((alpha, beta, gamma)) + } }