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 normal = hit.object.normal_at(point);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ use derive_builder::Builder;
|
||||
use crate::{
|
||||
lights::PointLight,
|
||||
patterns::StripePattern,
|
||||
shapes::Shape,
|
||||
tuples::{dot, reflect, Color, Tuple},
|
||||
Float, BLACK, WHITE,
|
||||
};
|
||||
@ -83,6 +84,7 @@ impl Default for Material {
|
||||
/// lights::PointLight,
|
||||
/// materials::{lighting, Material},
|
||||
/// patterns::stripe_pattern,
|
||||
/// shapes::Shape,
|
||||
/// tuples::{point, vector, Color},
|
||||
/// Float, BLACK, WHITE,
|
||||
/// };
|
||||
@ -90,40 +92,41 @@ impl Default for Material {
|
||||
/// let in_shadow = false;
|
||||
/// let m = Material::default();
|
||||
/// let position = point(0., 0., 0.);
|
||||
/// let object = Shape::sphere();
|
||||
///
|
||||
/// // Lighting with the eye between the light and the surface.
|
||||
/// let eyev = vector(0., 0., -1.);
|
||||
/// let normalv = vector(0., 0., -1.);
|
||||
/// 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));
|
||||
///
|
||||
/// // 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 normalv = vector(0., 0., -1.);
|
||||
/// 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);
|
||||
///
|
||||
/// // Lighting with the eye opposite surface, light offset 45°.
|
||||
/// let eyev = vector(0., 0., -1.);
|
||||
/// let normalv = vector(0., 0., -1.);
|
||||
/// 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));
|
||||
///
|
||||
/// // 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 normalv = vector(0., 0., -1.);
|
||||
/// 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));
|
||||
///
|
||||
/// // Lighting with the light behind the surface.
|
||||
/// let eyev = vector(0., 0., -1.);
|
||||
/// let normalv = vector(0., 0., -1.);
|
||||
/// 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));
|
||||
///
|
||||
/// // Lighting with the surface in shadow.
|
||||
@ -131,7 +134,7 @@ impl Default for Material {
|
||||
/// let eyev = vector(0., 0., -1.);
|
||||
/// let normalv = vector(0., 0., -1.);
|
||||
/// 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));
|
||||
///
|
||||
/// // Lighting with a pattern applied.
|
||||
@ -146,13 +149,30 @@ impl Default for Material {
|
||||
/// let eyev = vector(0., 0., -1.);
|
||||
/// let normalv = vector(0., 0., -1.);
|
||||
/// let light = PointLight::new(point(0., 0., -10.), WHITE);
|
||||
/// let c1 = lighting(&m, &light, point(0.9, 0., 0.), eyev, normalv, false);
|
||||
/// let c2 = lighting(&m, &light, point(1.1, 0., 0.), eyev, normalv, false);
|
||||
/// let c1 = lighting(
|
||||
/// &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!(c2, BLACK);
|
||||
/// ```
|
||||
pub fn lighting(
|
||||
material: &Material,
|
||||
object: &Shape,
|
||||
light: &PointLight,
|
||||
point: Tuple,
|
||||
eyev: Tuple,
|
||||
@ -162,7 +182,7 @@ pub fn lighting(
|
||||
// Combine the surface color with the light's color.
|
||||
let color = match &material.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;
|
||||
// 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)]
|
||||
pub struct StripePattern {
|
||||
pub a: Color,
|
||||
pub b: Color,
|
||||
transform: Matrix4x4,
|
||||
inverse_transform: Matrix4x4,
|
||||
}
|
||||
|
||||
/// 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);
|
||||
/// ```
|
||||
pub fn stripe_pattern(a: Color, b: Color) -> StripePattern {
|
||||
StripePattern { a, b }
|
||||
StripePattern {
|
||||
a,
|
||||
b,
|
||||
transform: Matrix4x4::identity(),
|
||||
inverse_transform: Matrix4x4::identity(),
|
||||
}
|
||||
}
|
||||
|
||||
impl StripePattern {
|
||||
@ -64,4 +75,54 @@ impl StripePattern {
|
||||
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
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@ -141,6 +141,7 @@ impl World {
|
||||
let shadowed = self.is_shadowed(comps.over_point, light);
|
||||
acc + lighting(
|
||||
&comps.object.material,
|
||||
&comps.object,
|
||||
light,
|
||||
comps.over_point,
|
||||
comps.eyev,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user