spheres: cache inverse transform to accelerate normal_at.

This commit is contained in:
2021-07-17 23:10:54 -07:00
parent 5f3bfd744e
commit 2395c96e01
4 changed files with 46 additions and 30 deletions

View File

@@ -6,11 +6,12 @@ use crate::{
tuples::{dot, Tuple},
};
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
/// Sphere represents the unit-sphere (radius of unit 1.) at the origin 0., 0., 0.
pub struct Sphere {
// TODO(wathiede): cache inverse to speed up intersect.
pub transform: Matrix4x4,
transform: Matrix4x4,
inverse_transform: Matrix4x4,
pub material: Material,
}
@@ -21,13 +22,13 @@ impl Default for Sphere {
///
/// // A sphere's default transform is the identity matrix.
/// let s = Sphere::default();
/// assert_eq!(s.transform, Matrix4x4::identity());
/// assert_eq!(s.transform(), Matrix4x4::identity());
///
/// // It can be changed by directly setting the transform member.
/// let mut s = Sphere::default();
/// let t = Matrix4x4::translation(2., 3., 4.);
/// s.transform = t.clone();
/// assert_eq!(s.transform, t);
/// s.set_transform ( t.clone());
/// assert_eq!(s.transform(), t);
///
/// // Default Sphere has the default material.
/// assert_eq!(s.material, Material::default());
@@ -42,6 +43,7 @@ impl Default for Sphere {
fn default() -> Sphere {
Sphere {
transform: Matrix4x4::identity(),
inverse_transform: Matrix4x4::identity(),
material: Material::default(),
}
}
@@ -91,7 +93,7 @@ impl Sphere {
///
/// // Compute the normal on a translated sphere.
/// let mut s = Sphere::default();
/// s.transform = Matrix4x4::translation(0., 1., 0.);
/// s.set_transform ( Matrix4x4::translation(0., 1., 0.));
/// let n = s.normal_at(Tuple::point(0., 1.70711, -0.70711));
/// assert_eq!(n, Tuple::vector(0., 0.70711, -0.70711));
@@ -99,17 +101,25 @@ impl Sphere {
/// use std::f32::consts::PI;
///
/// let mut s = Sphere::default();
/// s.transform = Matrix4x4::scaling(1.,0.5,1.) * Matrix4x4::rotation_z(PI/5.);
/// s.set_transform ( Matrix4x4::scaling(1.,0.5,1.) * Matrix4x4::rotation_z(PI/5.));
/// let n = s.normal_at(Tuple::point(0., 2_f32.sqrt()/2., -2_f32.sqrt()/2.));
/// assert_eq!(n, Tuple::vector(0., 0.97014, -0.24254));
/// ```
pub fn normal_at(&self, world_point: Tuple) -> Tuple {
let object_point = self.transform.inverse() * world_point;
let object_point = self.inverse_transform * world_point;
let object_normal = object_point - Tuple::point(0., 0., 0.);
let mut world_normal = self.transform.inverse().transpose() * object_normal;
let mut world_normal = self.inverse_transform.transpose() * object_normal;
world_normal.w = 0.;
world_normal.normalize()
}
pub fn transform(&self) -> Matrix4x4 {
self.transform
}
pub fn set_transform(&mut self, t: Matrix4x4) {
self.transform = t;
self.inverse_transform = t.inverse();
}
}
/// Intersect a ray with a sphere.
@@ -160,7 +170,7 @@ impl Sphere {
/// // Intersect a scaled sphere with a ray.
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
/// let mut s = Sphere::default();
/// s.transform = Matrix4x4::scaling(2., 2., 2.);
/// s.set_transform(Matrix4x4::scaling(2., 2., 2.));
/// let xs = intersect(&s, &r);
/// assert_eq!(xs.len(), 2, "xs {:?}", xs);
/// assert_eq!(xs[0].t, 3., "xs {:?}", xs);
@@ -169,12 +179,12 @@ impl Sphere {
/// // Intersect a translated sphere with a ray.
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
/// let mut s = Sphere::default();
/// s.transform = Matrix4x4::translation(5., 0., 0.);
/// s.set_transform(Matrix4x4::translation(5., 0., 0.));
/// let xs = intersect(&s, &r);
/// assert_eq!(xs.len(), 0);
/// ```
pub fn intersect<'s>(sphere: &'s Sphere, ray: &Ray) -> Intersections<'s> {
let ray = ray.transform(sphere.transform.inverse());
let ray = ray.transform(sphere.inverse_transform);
let sphere_to_ray = ray.origin - Tuple::point(0., 0., 0.);
let a = dot(ray.direction, ray.direction);
let b = 2. * dot(ray.direction, sphere_to_ray);

View File

@@ -48,7 +48,7 @@ impl World {
..Material::default()
};
let mut s2 = Sphere::default();
s2.transform = Matrix4x4::scaling(0.5, 0.5, 0.5);
s2.set_transform(Matrix4x4::scaling(0.5, 0.5, 0.5));
World {
light: Some(light),
objects: vec![s1, s2],