spheres: cache inverse transform to accelerate normal_at.
This commit is contained in:
parent
5f3bfd744e
commit
2395c96e01
@ -20,8 +20,9 @@ fn main() -> Result<()> {
|
|||||||
let half = wall_size / 2.;
|
let half = wall_size / 2.;
|
||||||
let color = Color::new(1., 0., 0.);
|
let color = Color::new(1., 0., 0.);
|
||||||
let mut shape = Sphere::default();
|
let mut shape = Sphere::default();
|
||||||
shape.transform =
|
shape.set_transform(
|
||||||
Matrix4x4::shearing(1., 0., 0., 0., 0., 0.) * Matrix4x4::scaling(0.5, 1., 1.0);
|
Matrix4x4::shearing(1., 0., 0., 0., 0., 0.) * Matrix4x4::scaling(0.5, 1., 1.0),
|
||||||
|
);
|
||||||
|
|
||||||
for y in 0..h {
|
for y in 0..h {
|
||||||
let world_y = half - pixel_size * y as f32;
|
let world_y = half - pixel_size * y as f32;
|
||||||
|
|||||||
@ -16,8 +16,8 @@ use rtchallenge::{
|
|||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let width = 640;
|
let width = 1024;
|
||||||
let height = 480;
|
let height = 1024;
|
||||||
let light_position = Tuple::point(-10., 10., -10.);
|
let light_position = Tuple::point(-10., 10., -10.);
|
||||||
let light_color = WHITE;
|
let light_color = WHITE;
|
||||||
let light = PointLight::new(light_position, light_color);
|
let light = PointLight::new(light_position, light_color);
|
||||||
@ -28,7 +28,7 @@ fn main() -> Result<()> {
|
|||||||
camera.transform = view_transform(from, to, up);
|
camera.transform = view_transform(from, to, up);
|
||||||
|
|
||||||
let mut floor = Sphere::default();
|
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 {
|
floor.material = Material {
|
||||||
color: Color::new(1., 0.9, 0.9),
|
color: Color::new(1., 0.9, 0.9),
|
||||||
specular: 0.,
|
specular: 0.,
|
||||||
@ -36,21 +36,25 @@ fn main() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut left_wall = Sphere::default();
|
let mut left_wall = Sphere::default();
|
||||||
left_wall.transform = Matrix4x4::translation(0., 0., 5.)
|
left_wall.set_transform(
|
||||||
* Matrix4x4::rotation_y(-PI / 4.)
|
Matrix4x4::translation(0., 0., 5.)
|
||||||
* Matrix4x4::rotation_x(PI / 2.)
|
* Matrix4x4::rotation_y(-PI / 4.)
|
||||||
* Matrix4x4::scaling(10., 0.01, 10.);
|
* Matrix4x4::rotation_x(PI / 2.)
|
||||||
|
* Matrix4x4::scaling(10., 0.01, 10.),
|
||||||
|
);
|
||||||
left_wall.material = floor.material.clone();
|
left_wall.material = floor.material.clone();
|
||||||
|
|
||||||
let mut right_wall = Sphere::default();
|
let mut right_wall = Sphere::default();
|
||||||
right_wall.transform = Matrix4x4::translation(0., 0., 5.)
|
right_wall.set_transform(
|
||||||
* Matrix4x4::rotation_y(PI / 4.)
|
Matrix4x4::translation(0., 0., 5.)
|
||||||
* Matrix4x4::rotation_x(PI / 2.)
|
* Matrix4x4::rotation_y(PI / 4.)
|
||||||
* Matrix4x4::scaling(10., 0.01, 10.);
|
* Matrix4x4::rotation_x(PI / 2.)
|
||||||
|
* Matrix4x4::scaling(10., 0.01, 10.),
|
||||||
|
);
|
||||||
right_wall.material = floor.material.clone();
|
right_wall.material = floor.material.clone();
|
||||||
|
|
||||||
let mut middle = Sphere::default();
|
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 {
|
middle.material = Material {
|
||||||
color: Color::new(0.1, 1., 0.5),
|
color: Color::new(0.1, 1., 0.5),
|
||||||
diffuse: 0.7,
|
diffuse: 0.7,
|
||||||
@ -59,7 +63,7 @@ fn main() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut right = Sphere::default();
|
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 {
|
right.material = Material {
|
||||||
color: Color::new(0.5, 1., 0.1),
|
color: Color::new(0.5, 1., 0.1),
|
||||||
diffuse: 0.7,
|
diffuse: 0.7,
|
||||||
@ -68,8 +72,9 @@ fn main() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut left = Sphere::default();
|
let mut left = Sphere::default();
|
||||||
left.transform =
|
left.set_transform(
|
||||||
Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33);
|
Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33),
|
||||||
|
);
|
||||||
left.material = Material {
|
left.material = Material {
|
||||||
color: Color::new(1., 0.8, 0.1),
|
color: Color::new(1., 0.8, 0.1),
|
||||||
diffuse: 0.7,
|
diffuse: 0.7,
|
||||||
|
|||||||
@ -6,11 +6,12 @@ use crate::{
|
|||||||
tuples::{dot, Tuple},
|
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.
|
/// Sphere represents the unit-sphere (radius of unit 1.) at the origin 0., 0., 0.
|
||||||
pub struct Sphere {
|
pub struct Sphere {
|
||||||
// TODO(wathiede): cache inverse to speed up intersect.
|
// TODO(wathiede): cache inverse to speed up intersect.
|
||||||
pub transform: Matrix4x4,
|
transform: Matrix4x4,
|
||||||
|
inverse_transform: Matrix4x4,
|
||||||
pub material: Material,
|
pub material: Material,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,13 +22,13 @@ impl Default for Sphere {
|
|||||||
///
|
///
|
||||||
/// // A sphere's default transform is the identity matrix.
|
/// // A sphere's default transform is the identity matrix.
|
||||||
/// let s = Sphere::default();
|
/// 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.
|
/// // It can be changed by directly setting the transform member.
|
||||||
/// let mut s = Sphere::default();
|
/// let mut s = Sphere::default();
|
||||||
/// let t = Matrix4x4::translation(2., 3., 4.);
|
/// let t = Matrix4x4::translation(2., 3., 4.);
|
||||||
/// s.transform = t.clone();
|
/// s.set_transform ( t.clone());
|
||||||
/// assert_eq!(s.transform, t);
|
/// assert_eq!(s.transform(), t);
|
||||||
///
|
///
|
||||||
/// // Default Sphere has the default material.
|
/// // Default Sphere has the default material.
|
||||||
/// assert_eq!(s.material, Material::default());
|
/// assert_eq!(s.material, Material::default());
|
||||||
@ -42,6 +43,7 @@ impl Default for Sphere {
|
|||||||
fn default() -> Sphere {
|
fn default() -> Sphere {
|
||||||
Sphere {
|
Sphere {
|
||||||
transform: Matrix4x4::identity(),
|
transform: Matrix4x4::identity(),
|
||||||
|
inverse_transform: Matrix4x4::identity(),
|
||||||
material: Material::default(),
|
material: Material::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +93,7 @@ impl Sphere {
|
|||||||
///
|
///
|
||||||
/// // Compute the normal on a translated sphere.
|
/// // Compute the normal on a translated sphere.
|
||||||
/// let mut s = Sphere::default();
|
/// 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));
|
/// let n = s.normal_at(Tuple::point(0., 1.70711, -0.70711));
|
||||||
/// assert_eq!(n, Tuple::vector(0., 0.70711, -0.70711));
|
/// assert_eq!(n, Tuple::vector(0., 0.70711, -0.70711));
|
||||||
|
|
||||||
@ -99,17 +101,25 @@ impl Sphere {
|
|||||||
/// use std::f32::consts::PI;
|
/// use std::f32::consts::PI;
|
||||||
///
|
///
|
||||||
/// let mut s = Sphere::default();
|
/// 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.));
|
/// 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));
|
/// assert_eq!(n, Tuple::vector(0., 0.97014, -0.24254));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn normal_at(&self, world_point: Tuple) -> Tuple {
|
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 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.w = 0.;
|
||||||
world_normal.normalize()
|
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.
|
/// Intersect a ray with a sphere.
|
||||||
@ -160,7 +170,7 @@ impl Sphere {
|
|||||||
/// // Intersect a scaled sphere with a ray.
|
/// // Intersect a scaled sphere with a ray.
|
||||||
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
||||||
/// let mut s = Sphere::default();
|
/// 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);
|
/// let xs = intersect(&s, &r);
|
||||||
/// assert_eq!(xs.len(), 2, "xs {:?}", xs);
|
/// assert_eq!(xs.len(), 2, "xs {:?}", xs);
|
||||||
/// assert_eq!(xs[0].t, 3., "xs {:?}", xs);
|
/// assert_eq!(xs[0].t, 3., "xs {:?}", xs);
|
||||||
@ -169,12 +179,12 @@ impl Sphere {
|
|||||||
/// // Intersect a translated sphere with a ray.
|
/// // Intersect a translated sphere with a ray.
|
||||||
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
||||||
/// let mut s = Sphere::default();
|
/// 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);
|
/// let xs = intersect(&s, &r);
|
||||||
/// assert_eq!(xs.len(), 0);
|
/// assert_eq!(xs.len(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn intersect<'s>(sphere: &'s Sphere, ray: &Ray) -> Intersections<'s> {
|
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 sphere_to_ray = ray.origin - Tuple::point(0., 0., 0.);
|
||||||
let a = dot(ray.direction, ray.direction);
|
let a = dot(ray.direction, ray.direction);
|
||||||
let b = 2. * dot(ray.direction, sphere_to_ray);
|
let b = 2. * dot(ray.direction, sphere_to_ray);
|
||||||
|
|||||||
@ -48,7 +48,7 @@ impl World {
|
|||||||
..Material::default()
|
..Material::default()
|
||||||
};
|
};
|
||||||
let mut s2 = Sphere::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 {
|
World {
|
||||||
light: Some(light),
|
light: Some(light),
|
||||||
objects: vec![s1, s2],
|
objects: vec![s1, s2],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user