patterns: implement object and pattern transformation awareness.

This commit is contained in:
Bill Thiede 2021-07-25 11:22:36 -07:00
parent bfa3282a37
commit 8b79876aee
5 changed files with 106 additions and 12 deletions

View File

@ -44,7 +44,15 @@ fn main() -> Result<()> {
let point = r.position(hit.t); let point = r.position(hit.t);
let normal = hit.object.normal_at(point); let normal = hit.object.normal_at(point);
let eye = -r.direction; let eye = -r.direction;
let color = lighting(&hit.object.material, &light, point, eye, normal, in_shadow); let color = lighting(
&hit.object.material,
&hit.object,
&light,
point,
eye,
normal,
in_shadow,
);
c.set(x, y, color); c.set(x, y, color);
} }
} }

View File

@ -3,6 +3,7 @@ use derive_builder::Builder;
use crate::{ use crate::{
lights::PointLight, lights::PointLight,
patterns::StripePattern, patterns::StripePattern,
shapes::Shape,
tuples::{dot, reflect, Color, Tuple}, tuples::{dot, reflect, Color, Tuple},
Float, BLACK, WHITE, Float, BLACK, WHITE,
}; };
@ -83,6 +84,7 @@ impl Default for Material {
/// lights::PointLight, /// lights::PointLight,
/// materials::{lighting, Material}, /// materials::{lighting, Material},
/// patterns::stripe_pattern, /// patterns::stripe_pattern,
/// shapes::Shape,
/// tuples::{point, vector, Color}, /// tuples::{point, vector, Color},
/// Float, BLACK, WHITE, /// Float, BLACK, WHITE,
/// }; /// };
@ -90,40 +92,41 @@ impl Default for Material {
/// let in_shadow = false; /// let in_shadow = false;
/// let m = Material::default(); /// let m = Material::default();
/// let position = point(0., 0., 0.); /// let position = point(0., 0., 0.);
/// let object = Shape::sphere();
/// ///
/// // Lighting with the eye between the light and the surface. /// // Lighting with the eye between the light and the surface.
/// let eyev = vector(0., 0., -1.); /// let eyev = vector(0., 0., -1.);
/// let normalv = vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(point(0., 0., -10.), WHITE); /// let light = PointLight::new(point(0., 0., -10.), WHITE);
/// let result = lighting(&m, &light, position, eyev, normalv, in_shadow); /// let result = lighting(&m, &object, &light, position, eyev, normalv, in_shadow);
/// assert_eq!(result, Color::new(1.9, 1.9, 1.9)); /// assert_eq!(result, Color::new(1.9, 1.9, 1.9));
/// ///
/// // Lighting with the eye between the light and the surface, eye offset 45°. /// // Lighting with the eye between the light and the surface, eye offset 45°.
/// let eyev = vector(0., (2. as Float).sqrt() / 2., -(2. as Float).sqrt() / 2.); /// let eyev = vector(0., (2. as Float).sqrt() / 2., -(2. as Float).sqrt() / 2.);
/// let normalv = vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(point(0., 0., -10.), WHITE); /// let light = PointLight::new(point(0., 0., -10.), WHITE);
/// let result = lighting(&m, &light, position, eyev, normalv, in_shadow); /// let result = lighting(&m, &object, &light, position, eyev, normalv, in_shadow);
/// assert_eq!(result, WHITE); /// assert_eq!(result, WHITE);
/// ///
/// // Lighting with the eye opposite surface, light offset 45°. /// // Lighting with the eye opposite surface, light offset 45°.
/// let eyev = vector(0., 0., -1.); /// let eyev = vector(0., 0., -1.);
/// let normalv = vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(point(0., 10., -10.), WHITE); /// let light = PointLight::new(point(0., 10., -10.), WHITE);
/// let result = lighting(&m, &light, position, eyev, normalv, in_shadow); /// let result = lighting(&m, &object, &light, position, eyev, normalv, in_shadow);
/// assert_eq!(result, Color::new(0.7364, 0.7364, 0.7364)); /// assert_eq!(result, Color::new(0.7364, 0.7364, 0.7364));
/// ///
/// // Lighting with the eye in the path of the reflection vector. /// // Lighting with the eye in the path of the reflection vector.
/// let eyev = vector(0., -(2.0 as Float).sqrt() / 2., -(2.0 as Float).sqrt() / 2.); /// let eyev = vector(0., -(2.0 as Float).sqrt() / 2., -(2.0 as Float).sqrt() / 2.);
/// let normalv = vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(point(0., 10., -10.), WHITE); /// let light = PointLight::new(point(0., 10., -10.), WHITE);
/// let result = lighting(&m, &light, position, eyev, normalv, in_shadow); /// let result = lighting(&m, &object, &light, position, eyev, normalv, in_shadow);
/// assert_eq!(result, Color::new(1.63639, 1.63639, 1.63639)); /// assert_eq!(result, Color::new(1.63639, 1.63639, 1.63639));
/// ///
/// // Lighting with the light behind the surface. /// // Lighting with the light behind the surface.
/// let eyev = vector(0., 0., -1.); /// let eyev = vector(0., 0., -1.);
/// let normalv = vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(point(0., 0., 10.), WHITE); /// let light = PointLight::new(point(0., 0., 10.), WHITE);
/// let result = lighting(&m, &light, position, eyev, normalv, in_shadow); /// let result = lighting(&m, &object, &light, position, eyev, normalv, in_shadow);
/// assert_eq!(result, Color::new(0.1, 0.1, 0.1)); /// assert_eq!(result, Color::new(0.1, 0.1, 0.1));
/// ///
/// // Lighting with the surface in shadow. /// // Lighting with the surface in shadow.
@ -131,7 +134,7 @@ impl Default for Material {
/// let eyev = vector(0., 0., -1.); /// let eyev = vector(0., 0., -1.);
/// let normalv = vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(point(0., 0., -10.), WHITE); /// let light = PointLight::new(point(0., 0., -10.), WHITE);
/// let result = lighting(&m, &light, position, eyev, normalv, in_shadow); /// let result = lighting(&m, &object, &light, position, eyev, normalv, in_shadow);
/// assert_eq!(result, Color::new(0.1, 0.1, 0.1)); /// assert_eq!(result, Color::new(0.1, 0.1, 0.1));
/// ///
/// // Lighting with a pattern applied. /// // Lighting with a pattern applied.
@ -146,13 +149,30 @@ impl Default for Material {
/// let eyev = vector(0., 0., -1.); /// let eyev = vector(0., 0., -1.);
/// let normalv = vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(point(0., 0., -10.), WHITE); /// let light = PointLight::new(point(0., 0., -10.), WHITE);
/// let c1 = lighting(&m, &light, point(0.9, 0., 0.), eyev, normalv, false); /// let c1 = lighting(
/// let c2 = lighting(&m, &light, point(1.1, 0., 0.), eyev, normalv, false); /// &m,
/// &object,
/// &light,
/// point(0.9, 0., 0.),
/// eyev,
/// normalv,
/// false,
/// );
/// let c2 = lighting(
/// &m,
/// &object,
/// &light,
/// point(1.1, 0., 0.),
/// eyev,
/// normalv,
/// false,
/// );
/// assert_eq!(c1, WHITE); /// assert_eq!(c1, WHITE);
/// assert_eq!(c2, BLACK); /// assert_eq!(c2, BLACK);
/// ``` /// ```
pub fn lighting( pub fn lighting(
material: &Material, material: &Material,
object: &Shape,
light: &PointLight, light: &PointLight,
point: Tuple, point: Tuple,
eyev: Tuple, eyev: Tuple,
@ -162,7 +182,7 @@ pub fn lighting(
// Combine the surface color with the light's color. // Combine the surface color with the light's color.
let color = match &material.color { let color = match &material.color {
ColorMapper::Constant(color) => *color, ColorMapper::Constant(color) => *color,
ColorMapper::StripePattern(pat) => pat.stripe_at(point), ColorMapper::StripePattern(pat) => pat.stripe_at_object(object, point),
}; };
let effective_color = color * light.intensity; let effective_color = color * light.intensity;
// Find the direciton of the light source. // Find the direciton of the light source.

View File

@ -1,9 +1,15 @@
use crate::tuples::{Color, Tuple}; use crate::{
matrices::Matrix4x4,
shapes::Shape,
tuples::{Color, Tuple},
};
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct StripePattern { pub struct StripePattern {
pub a: Color, pub a: Color,
pub b: Color, pub b: Color,
transform: Matrix4x4,
inverse_transform: Matrix4x4,
} }
/// Create a material pattern that alternates between the given colors along each unit of the /// Create a material pattern that alternates between the given colors along each unit of the
@ -18,7 +24,12 @@ pub struct StripePattern {
/// assert_eq!(pattern.b, WHITE); /// assert_eq!(pattern.b, WHITE);
/// ``` /// ```
pub fn stripe_pattern(a: Color, b: Color) -> StripePattern { pub fn stripe_pattern(a: Color, b: Color) -> StripePattern {
StripePattern { a, b } StripePattern {
a,
b,
transform: Matrix4x4::identity(),
inverse_transform: Matrix4x4::identity(),
}
} }
impl StripePattern { impl StripePattern {
@ -64,4 +75,54 @@ impl StripePattern {
self.b self.b
} }
} }
/// Sample the color at the given world point on the given object.
/// This function respects the object and the pattern's transform matrix.
///
/// # Examples
/// ```
/// use rtchallenge::{
/// matrices::scaling,
/// patterns::stripe_pattern,
/// shapes::{Shape, ShapeBuilder},
/// tuples::point,
/// BLACK, WHITE,
/// };
///
/// # fn main() -> Result<(), Box<std::error::Error>> {
///
/// // Stripes with an object transformation.
/// let object = ShapeBuilder::sphere()
/// .transform(scaling(2., 2., 2.))
/// .build()?;
/// let pattern = stripe_pattern(WHITE, BLACK);
/// let c = pattern.stripe_at_object(&object, point(1.5, 0., 0.));
/// assert_eq!(c, WHITE);
///
/// // Stripes with an pattern transformation.
/// let object = Shape::sphere();
/// let mut pattern = stripe_pattern(WHITE, BLACK);
/// pattern.set_transform(scaling(2., 2., 2.));
/// let c = pattern.stripe_at_object(&object, point(1.5, 0., 0.));
/// assert_eq!(c, WHITE);
///
/// # Ok(())
/// # }
/// ```
pub fn stripe_at_object(&self, object: &Shape, world_point: Tuple) -> Color {
let object_point = object.inverse_transform() * world_point;
let pattern_point = self.inverse_transform * object_point;
self.stripe_at(pattern_point)
}
pub fn transform(&self) -> Matrix4x4 {
self.transform
}
pub fn inverse_transform(&self) -> Matrix4x4 {
self.inverse_transform
}
pub fn set_transform(&mut self, t: Matrix4x4) {
self.transform = t;
self.inverse_transform = t.inverse();
}
} }

View File

@ -347,6 +347,10 @@ impl Shape {
self.transform self.transform
} }
pub fn inverse_transform(&self) -> Matrix4x4 {
self.inverse_transform
}
pub fn set_transform(&mut self, t: Matrix4x4) { pub fn set_transform(&mut self, t: Matrix4x4) {
self.transform = t; self.transform = t;
self.inverse_transform = t.inverse(); self.inverse_transform = t.inverse();

View File

@ -141,6 +141,7 @@ impl World {
let shadowed = self.is_shadowed(comps.over_point, light); let shadowed = self.is_shadowed(comps.over_point, light);
acc + lighting( acc + lighting(
&comps.object.material, &comps.object.material,
&comps.object,
light, light,
comps.over_point, comps.over_point,
comps.eyev, comps.eyev,