patterns: create generic Pattern modeled after StripePattern.
Add TestPattern to validate generic implementation. Make Material.color use Pattern.
This commit is contained in:
parent
8b79876aee
commit
b9f2c3f0ec
@ -2,44 +2,17 @@ use derive_builder::Builder;
|
||||
|
||||
use crate::{
|
||||
lights::PointLight,
|
||||
patterns::StripePattern,
|
||||
patterns::Pattern,
|
||||
shapes::Shape,
|
||||
tuples::{dot, reflect, Color, Tuple},
|
||||
Float, BLACK, WHITE,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ColorMapper {
|
||||
Constant(Color),
|
||||
StripePattern(StripePattern),
|
||||
}
|
||||
|
||||
/// Creates a [ColorMapper::Constant] from the given [Color]
|
||||
impl From<Color> for ColorMapper {
|
||||
fn from(c: Color) -> Self {
|
||||
ColorMapper::Constant(c)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [ColorMapper::Constant] from the given array.
|
||||
impl From<[Float; 3]> for ColorMapper {
|
||||
fn from(rgb: [Float; 3]) -> Self {
|
||||
ColorMapper::Constant(rgb.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [ColorMapper::StripePattern] from the given [Color]
|
||||
impl From<StripePattern> for ColorMapper {
|
||||
fn from(sp: StripePattern) -> Self {
|
||||
ColorMapper::StripePattern(sp)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Builder, Debug, PartialEq, Clone)]
|
||||
#[builder(default)]
|
||||
pub struct Material {
|
||||
#[builder(setter(into))]
|
||||
pub color: ColorMapper,
|
||||
pub color: Pattern,
|
||||
pub ambient: Float,
|
||||
pub diffuse: Float,
|
||||
pub specular: Float,
|
||||
@ -180,10 +153,7 @@ pub fn lighting(
|
||||
in_shadow: bool,
|
||||
) -> Color {
|
||||
// Combine the surface color with the light's color.
|
||||
let color = match &material.color {
|
||||
ColorMapper::Constant(color) => *color,
|
||||
ColorMapper::StripePattern(pat) => pat.stripe_at_object(object, point),
|
||||
};
|
||||
let color = material.color.pattern_at_object(object, point);
|
||||
let effective_color = color * light.intensity;
|
||||
// Find the direciton of the light source.
|
||||
let lightv = (light.position - point).normalize();
|
||||
|
||||
@ -2,38 +2,100 @@ use crate::{
|
||||
matrices::Matrix4x4,
|
||||
shapes::Shape,
|
||||
tuples::{Color, Tuple},
|
||||
Float, WHITE,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct StripePattern {
|
||||
pub a: Color,
|
||||
pub b: Color,
|
||||
pub enum ColorMapper {
|
||||
/// TestPattern the color returned is the pattern space point after going through world->object and object->pattern space translation.
|
||||
TestPattern,
|
||||
/// Solid color, the same sampled every where.
|
||||
Constant(Color),
|
||||
/// Pattern that alternates between the given colors along each unit of the X-axis. The strip
|
||||
/// extends infinitely in the positive and negative Y and Z axes.
|
||||
Stripe { a: Color, b: Color },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Pattern {
|
||||
pub color: ColorMapper,
|
||||
transform: Matrix4x4,
|
||||
inverse_transform: Matrix4x4,
|
||||
}
|
||||
|
||||
impl Default for Pattern {
|
||||
fn default() -> Pattern {
|
||||
Pattern {
|
||||
color: ColorMapper::Constant(WHITE),
|
||||
transform: Matrix4x4::identity(),
|
||||
inverse_transform: Matrix4x4::identity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [Pattern] with a color type of [ColorMapper::Constant] from the given [Color]
|
||||
impl From<[Float; 3]> for Pattern {
|
||||
fn from(rgb: [Float; 3]) -> Self {
|
||||
Pattern {
|
||||
color: ColorMapper::Constant(rgb.into()),
|
||||
..Pattern::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Creates a [Pattern] with a color type of [ColorMapper::Constant] from the given [Color]
|
||||
impl From<Color> for Pattern {
|
||||
fn from(c: Color) -> Self {
|
||||
Pattern {
|
||||
color: ColorMapper::Constant(c),
|
||||
..Pattern::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a material pattern that alternates between the given colors along each unit of the
|
||||
/// X-axis. The strip extends infinitely in the positive and negative Y and Z axes.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::{patterns::stripe_pattern, BLACK, WHITE};
|
||||
/// use rtchallenge::{
|
||||
/// patterns::{stripe_pattern, ColorMapper},
|
||||
/// BLACK, WHITE,
|
||||
/// };
|
||||
///
|
||||
/// let pattern = stripe_pattern(BLACK, WHITE);
|
||||
/// assert_eq!(pattern.a, BLACK);
|
||||
/// assert_eq!(pattern.b, WHITE);
|
||||
/// assert_eq!(pattern.color, ColorMapper::Stripe { a: BLACK, b: WHITE });
|
||||
/// ```
|
||||
pub fn stripe_pattern(a: Color, b: Color) -> StripePattern {
|
||||
StripePattern {
|
||||
a,
|
||||
b,
|
||||
transform: Matrix4x4::identity(),
|
||||
inverse_transform: Matrix4x4::identity(),
|
||||
pub fn stripe_pattern(a: Color, b: Color) -> Pattern {
|
||||
Pattern {
|
||||
color: ColorMapper::Stripe { a, b },
|
||||
..Pattern::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl StripePattern {
|
||||
/// Sample the color at the given point.
|
||||
/// Create a material pattern used for testing. The color returned is the pattern space point
|
||||
/// after going through world->object and object->pattern space translation.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::{
|
||||
/// matrices::Matrix4x4,
|
||||
/// patterns::{test_pattern, ColorMapper},
|
||||
/// BLACK, WHITE,
|
||||
/// };
|
||||
///
|
||||
/// let pattern = test_pattern();
|
||||
/// assert_eq!(pattern.transform(), Matrix4x4::identity());
|
||||
/// ```
|
||||
pub fn test_pattern() -> Pattern {
|
||||
Pattern {
|
||||
color: ColorMapper::TestPattern,
|
||||
..Pattern::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic implementation for mapping points to colors according to the given [ColorMapper].
|
||||
impl Pattern {
|
||||
/// Sample the color at the given point in untranslated object space.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -58,21 +120,29 @@ impl StripePattern {
|
||||
/// (point(-1., 0., 0.), BLACK),
|
||||
/// (point(-1.1, 0., 0.), WHITE),
|
||||
/// ] {
|
||||
/// assert_eq!(pattern.stripe_at(*p), *want, "{:?}", p);
|
||||
/// assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn stripe_at(&self, point: Tuple) -> Color {
|
||||
let x = point.x.floor() as i64;
|
||||
// Shift negative valued to get correct striping.
|
||||
if x < 0 {
|
||||
x - 1
|
||||
} else {
|
||||
x
|
||||
};
|
||||
if x % 2 == 0 {
|
||||
self.a
|
||||
} else {
|
||||
self.b
|
||||
pub fn pattern_at(&self, point: Tuple) -> Color {
|
||||
match self.color {
|
||||
ColorMapper::TestPattern => [point.x, point.y, point.z].into(),
|
||||
ColorMapper::Constant(c) => c,
|
||||
ColorMapper::Stripe { a, b } => {
|
||||
let x = point.x.floor() as i64;
|
||||
/*
|
||||
// Shift negative valued to get correct striping.
|
||||
if x < 0 {
|
||||
x - 1
|
||||
} else {
|
||||
x
|
||||
};
|
||||
*/
|
||||
if x % 2 == 0 {
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Sample the color at the given world point on the given object.
|
||||
@ -95,23 +165,23 @@ impl StripePattern {
|
||||
/// .transform(scaling(2., 2., 2.))
|
||||
/// .build()?;
|
||||
/// let pattern = stripe_pattern(WHITE, BLACK);
|
||||
/// let c = pattern.stripe_at_object(&object, point(1.5, 0., 0.));
|
||||
/// let c = pattern.pattern_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.));
|
||||
/// let c = pattern.pattern_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 {
|
||||
pub fn pattern_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)
|
||||
self.pattern_at(pattern_point)
|
||||
}
|
||||
pub fn transform(&self) -> Matrix4x4 {
|
||||
self.transform
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user