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.
(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

View file

@ -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,

View file

@ -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),
}
}
}

View file

@ -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<usize>,
/// Indexes into the scene's normal coordinates list
pub normals: Option<Vector3<usize>>,
/// Indexes into the scene's texture coordinates list
pub textures: Option<Vector3<usize>>,
}
@ -21,17 +26,13 @@ impl Triangle {
scene: &Scene,
ray: &Ray,
) -> Result<Option<IntersectionContext>> {
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<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))
}
}