From 2395c96e017a34b48fee0acfb08e9316161860f6 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sat, 17 Jul 2021 23:10:54 -0700 Subject: [PATCH] spheres: cache inverse transform to accelerate `normal_at`. --- rtchallenge/examples/eoc5.rs | 5 +++-- rtchallenge/examples/eoc7.rs | 35 ++++++++++++++++++++--------------- rtchallenge/src/spheres.rs | 34 ++++++++++++++++++++++------------ rtchallenge/src/world.rs | 2 +- 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/rtchallenge/examples/eoc5.rs b/rtchallenge/examples/eoc5.rs index ea74342..e1d3251 100644 --- a/rtchallenge/examples/eoc5.rs +++ b/rtchallenge/examples/eoc5.rs @@ -20,8 +20,9 @@ fn main() -> Result<()> { let half = wall_size / 2.; let color = Color::new(1., 0., 0.); let mut shape = Sphere::default(); - shape.transform = - Matrix4x4::shearing(1., 0., 0., 0., 0., 0.) * Matrix4x4::scaling(0.5, 1., 1.0); + shape.set_transform( + Matrix4x4::shearing(1., 0., 0., 0., 0., 0.) * Matrix4x4::scaling(0.5, 1., 1.0), + ); for y in 0..h { let world_y = half - pixel_size * y as f32; diff --git a/rtchallenge/examples/eoc7.rs b/rtchallenge/examples/eoc7.rs index ebc1435..9c63b31 100644 --- a/rtchallenge/examples/eoc7.rs +++ b/rtchallenge/examples/eoc7.rs @@ -16,8 +16,8 @@ use rtchallenge::{ fn main() -> Result<()> { let start = Instant::now(); - let width = 640; - let height = 480; + let width = 1024; + let height = 1024; let light_position = Tuple::point(-10., 10., -10.); let light_color = WHITE; let light = PointLight::new(light_position, light_color); @@ -28,7 +28,7 @@ fn main() -> Result<()> { camera.transform = view_transform(from, to, up); let mut floor = Sphere::default(); - floor.transform = Matrix4x4::scaling(10., 0.01, 10.); + floor.set_transform(Matrix4x4::scaling(10., 0.01, 10.)); floor.material = Material { color: Color::new(1., 0.9, 0.9), specular: 0., @@ -36,21 +36,25 @@ fn main() -> Result<()> { }; let mut left_wall = Sphere::default(); - left_wall.transform = Matrix4x4::translation(0., 0., 5.) - * Matrix4x4::rotation_y(-PI / 4.) - * Matrix4x4::rotation_x(PI / 2.) - * Matrix4x4::scaling(10., 0.01, 10.); + left_wall.set_transform( + Matrix4x4::translation(0., 0., 5.) + * Matrix4x4::rotation_y(-PI / 4.) + * Matrix4x4::rotation_x(PI / 2.) + * Matrix4x4::scaling(10., 0.01, 10.), + ); left_wall.material = floor.material.clone(); let mut right_wall = Sphere::default(); - right_wall.transform = Matrix4x4::translation(0., 0., 5.) - * Matrix4x4::rotation_y(PI / 4.) - * Matrix4x4::rotation_x(PI / 2.) - * Matrix4x4::scaling(10., 0.01, 10.); + right_wall.set_transform( + Matrix4x4::translation(0., 0., 5.) + * Matrix4x4::rotation_y(PI / 4.) + * Matrix4x4::rotation_x(PI / 2.) + * Matrix4x4::scaling(10., 0.01, 10.), + ); right_wall.material = floor.material.clone(); let mut middle = Sphere::default(); - middle.transform = Matrix4x4::translation(-0.5, 1., 0.5); + middle.set_transform(Matrix4x4::translation(-0.5, 1., 0.5)); middle.material = Material { color: Color::new(0.1, 1., 0.5), diffuse: 0.7, @@ -59,7 +63,7 @@ fn main() -> Result<()> { }; let mut right = Sphere::default(); - right.transform = Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5); + right.set_transform(Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5)); right.material = Material { color: Color::new(0.5, 1., 0.1), diffuse: 0.7, @@ -68,8 +72,9 @@ fn main() -> Result<()> { }; let mut left = Sphere::default(); - left.transform = - Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33); + left.set_transform( + Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33), + ); left.material = Material { color: Color::new(1., 0.8, 0.1), diffuse: 0.7, diff --git a/rtchallenge/src/spheres.rs b/rtchallenge/src/spheres.rs index 0888a89..9a27f33 100644 --- a/rtchallenge/src/spheres.rs +++ b/rtchallenge/src/spheres.rs @@ -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); diff --git a/rtchallenge/src/world.rs b/rtchallenge/src/world.rs index 91c27b2..83bc3e5 100644 --- a/rtchallenge/src/world.rs +++ b/rtchallenge/src/world.rs @@ -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],