patterns: implement object and pattern transformation awareness.
This commit is contained in:
parent
bfa3282a37
commit
8b79876aee
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user