Implement textured coordinates

This commit is contained in:
Michael Zhang 2023-02-25 17:16:36 -06:00
parent 26ddd8d236
commit a52863fe6c
4 changed files with 90 additions and 21 deletions

View file

@ -34,7 +34,7 @@ Due: Friday March 3rd
color in the Phong illumination model when it is evaluated at that point. color in the Phong illumination model when it is evaluated at that point.
(25 pts) (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 coordinate at the ray/triangle intersection point is correctly
interpolated from the texture coordinates defined at each of the three interpolated from the texture coordinates defined at each of the three
triangle vertices. The interpolated texture coordinate is used to retrieve triangle vertices. The interpolated texture coordinate is used to retrieve

View file

@ -37,7 +37,9 @@ impl Scene {
let diffuse_color = match object.texture_idx { let diffuse_color = match object.texture_idx {
Some(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) { let texture = match self.textures.get(texture_idx) {
Some(v) => v, Some(v) => v,

View file

@ -49,12 +49,13 @@ impl ObjectKind {
/// the intersection point /// the intersection point
pub fn get_texture_coord( pub fn get_texture_coord(
&self, &self,
scene: &Scene,
ctx: &IntersectionContext, ctx: &IntersectionContext,
) -> Result<(f64, f64)> { ) -> Result<(f64, f64)> {
match self { match self {
ObjectKind::Sphere(sphere) => sphere.get_texture_coord(ctx), ObjectKind::Sphere(sphere) => sphere.get_texture_coord(ctx),
ObjectKind::Cylinder(cylinder) => todo!(), ObjectKind::Cylinder(cylinder) => todo!(),
ObjectKind::Triangle(triangle) => todo!(), ObjectKind::Triangle(triangle) => triangle.get_texture_coord(scene, ctx),
} }
} }
} }

View file

@ -4,6 +4,7 @@ use ordered_float::NotNan;
use crate::ray::Ray; use crate::ray::Ray;
use crate::utils::{cross, dot}; use crate::utils::{cross, dot};
use crate::{Point, Vector};
use super::illumination::IntersectionContext; use super::illumination::IntersectionContext;
use super::Scene; use super::Scene;
@ -11,7 +12,11 @@ use super::Scene;
#[derive(Debug)] #[derive(Debug)]
pub struct Triangle { pub struct Triangle {
pub vertices: Vector3<usize>, pub vertices: Vector3<usize>,
/// Indexes into the scene's normal coordinates list
pub normals: Option<Vector3<usize>>, pub normals: Option<Vector3<usize>>,
/// Indexes into the scene's texture coordinates list
pub textures: Option<Vector3<usize>>, pub textures: Option<Vector3<usize>>,
} }
@ -21,17 +26,13 @@ impl Triangle {
scene: &Scene, scene: &Scene,
ray: &Ray, ray: &Ray,
) -> Result<Option<IntersectionContext>> { ) -> Result<Option<IntersectionContext>> {
let p0 = scene.triangle_vertices[self.vertices.x]; let (p0, e1, e2) = self.basis_vectors(scene);
let p1 = scene.triangle_vertices[self.vertices.y];
let p2 = scene.triangle_vertices[self.vertices.z];
// Solve for the plane equation coefficients A, B, C, D such that: // Solve for the plane equation coefficients A, B, C, D such that:
// //
// $$ // $$
// Ax + By + Cz + D = 0 // Ax + By + Cz + D = 0
// $$ // $$
let e1 = p1 - p0;
let e2 = p2 - p0;
let n = cross(e1, e2); let n = cross(e1, e2);
let a = n.x; let a = n.x;
let b = n.y; let b = n.y;
@ -63,21 +64,10 @@ impl Triangle {
// p = p0 + beta * e1 + gamma * e2 // p = p0 + beta * e1 + gamma * e2
// Using the whack linear algebra approach derived on slide 57 // Using the whack linear algebra approach derived on slide 57
let ep = point - p0; 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 p = Vector2::new(dot(e1, ep), dot(e2, ep));
let d_inv = match d.try_inverse() { let (alpha, beta, gamma) =
Some(v) => v, self.compute_barycentric_coordinates(scene, p)?;
// 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;
// Each of alpha, beta, and gamma must be between 0 and 1 // 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 < v && v < 1.0) {
@ -103,4 +93,80 @@ impl Triangle {
normal, 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<f64> {
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<f64>,
) -> 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))
}
} }