library: shape: add InterpolatedTriangle type
This is a triangle with added normal interpolation at its edges. This is particularly useful when rendering mesh objects.
This commit is contained in:
parent
3b5410aef9
commit
5c0fc9689e
153
pathtracer/src/shape/interpolated_triangle.rs
Normal file
153
pathtracer/src/shape/interpolated_triangle.rs
Normal file
|
@ -0,0 +1,153 @@
|
|||
use super::triangle::Triangle;
|
||||
use super::Shape;
|
||||
use crate::{Point, Point2D, Vector};
|
||||
use beevee::aabb::AABB;
|
||||
use beevee::ray::Ray;
|
||||
use nalgebra::Unit;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// Represent a triangle with interpolated normals inside the scene.
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
pub struct InterpolatedTriangle {
|
||||
#[serde(flatten)]
|
||||
tri: Triangle,
|
||||
// FIXME: serialize with unit
|
||||
normals: [Unit<Vector>; 3],
|
||||
}
|
||||
|
||||
impl InterpolatedTriangle {
|
||||
/// Creates a new `InterpolatedTriangle` from 3 [`Point`]s and 3 [`Vector`]s.
|
||||
///
|
||||
/// [`Point`]: ../../type.Point.html
|
||||
/// [`Point`]: ../../type.Vector.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pathtracer::shape::InterpolatedTriangle;
|
||||
/// # use pathtracer::{Point, Vector};
|
||||
/// #
|
||||
/// let t = InterpolatedTriangle::new(
|
||||
/// Point::new(1.0, 0.0, 0.0),
|
||||
/// Point::new(0.0, 1.0, 0.0),
|
||||
/// Point::new(0.0, 0.0, 1.0),
|
||||
/// Vector::x_axis(),
|
||||
/// Vector::y_axis(),
|
||||
/// Vector::z_axis(),
|
||||
/// );
|
||||
/// ```
|
||||
pub fn new(
|
||||
c0: Point,
|
||||
c1: Point,
|
||||
c2: Point,
|
||||
n0: Unit<Vector>,
|
||||
n1: Unit<Vector>,
|
||||
n2: Unit<Vector>,
|
||||
) -> Self {
|
||||
InterpolatedTriangle {
|
||||
tri: Triangle::new(c0, c1, c2),
|
||||
normals: [n0, n1, n2],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Shape for InterpolatedTriangle {
|
||||
fn intersect(&self, ray: &Ray) -> Option<f32> {
|
||||
self.tri.intersect(ray)
|
||||
}
|
||||
|
||||
fn normal(&self, point: &Point) -> Unit<Vector> {
|
||||
let (u, v) = {
|
||||
let c = self.tri.barycentric(point);
|
||||
(c.x, c.y)
|
||||
};
|
||||
let interpol = self.normals[0].as_ref() * (1. - u - v)
|
||||
+ self.normals[1].as_ref() * u
|
||||
+ self.normals[2].as_ref() * v;
|
||||
Unit::new_normalize(interpol)
|
||||
}
|
||||
|
||||
fn project_texel(&self, point: &Point) -> Point2D {
|
||||
self.tri.project_texel(point)
|
||||
}
|
||||
|
||||
fn aabb(&self) -> AABB {
|
||||
self.tri.aabb()
|
||||
}
|
||||
|
||||
fn centroid(&self) -> Point {
|
||||
self.tri.centroid()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn simple_triangle() -> InterpolatedTriangle {
|
||||
InterpolatedTriangle::new(
|
||||
Point::origin(),
|
||||
Point::new(0., 1., 1.),
|
||||
Point::new(0., 1., 0.),
|
||||
Vector::x_axis(),
|
||||
Vector::y_axis(),
|
||||
Vector::z_axis(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_interpolation_at_c0_works() {
|
||||
let triangle = simple_triangle();
|
||||
let normal = triangle.normal(&Point::origin());
|
||||
assert_eq!(normal, Vector::x_axis());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_interpolation_at_c1_works() {
|
||||
let triangle = simple_triangle();
|
||||
let normal = triangle.normal(&Point::new(0., 1., 1.));
|
||||
assert_eq!(normal, Vector::y_axis());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_interpolation_at_c2_works() {
|
||||
let triangle = simple_triangle();
|
||||
let normal = triangle.normal(&Point::new(0., 1., 0.));
|
||||
assert_eq!(normal, Vector::z_axis());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_interpolation_at_center_works() {
|
||||
let triangle = simple_triangle();
|
||||
let center = Point::new(0., 2. / 3., 1. / 3.);
|
||||
let normal = triangle.normal(¢er);
|
||||
let expected = Unit::new_normalize(Vector::new(1., 1., 1.));
|
||||
assert!((normal.as_ref() - expected.as_ref()).magnitude() < 1e-5)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization_works() {
|
||||
let yaml = r#"
|
||||
corners:
|
||||
- [0.0, 0.0, 0.0]
|
||||
- [0.0, 1.0, 1.0]
|
||||
- [0.0, 1.0, 0.0]
|
||||
normals:
|
||||
- [1.0, 0.0, 0.0]
|
||||
- [0.0, 1.0, 0.0]
|
||||
- [0.0, 0.0, 1.0]
|
||||
"#;
|
||||
let triangle: InterpolatedTriangle = serde_yaml::from_str(yaml).unwrap();
|
||||
assert_eq!(
|
||||
triangle,
|
||||
InterpolatedTriangle::new(
|
||||
Point::origin(),
|
||||
Point::new(0., 1., 1.),
|
||||
Point::new(0., 1., 0.),
|
||||
Vector::x_axis(),
|
||||
Vector::y_axis(),
|
||||
Vector::z_axis(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ use serde::Deserialize;
|
|||
pub enum ShapeEnum {
|
||||
Sphere,
|
||||
Triangle,
|
||||
InterpolatedTriangle,
|
||||
}
|
||||
|
||||
/// Represent an abstract shape inside the scene.
|
||||
|
@ -51,6 +52,9 @@ impl Intersected for dyn Shape {
|
|||
}
|
||||
}
|
||||
|
||||
mod interpolated_triangle;
|
||||
pub use interpolated_triangle::*;
|
||||
|
||||
mod sphere;
|
||||
pub use sphere::*;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ impl Triangle {
|
|||
}
|
||||
}
|
||||
|
||||
fn barycentric(&self, point: &Point) -> Point2D {
|
||||
pub(crate) fn barycentric(&self, point: &Point) -> Point2D {
|
||||
let c0_pos = point - self.c0;
|
||||
// P - A = u * (B - A) + v * (C - A)
|
||||
// (C - A) = v0 is c0c2
|
||||
|
|
Loading…
Reference in a new issue