From bfa3282a3770de3a7fea33e093ea506267533c02 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sat, 24 Jul 2021 19:36:32 -0700 Subject: [PATCH] materials: add StripePattern as a Material color option. --- rtchallenge/examples/eoc6.rs | 2 +- rtchallenge/examples/eoc7.rs | 8 +-- rtchallenge/examples/eoc8.rs | 8 +-- rtchallenge/examples/eoc9.rs | 10 ++-- rtchallenge/src/lib.rs | 1 + rtchallenge/src/materials.rs | 103 ++++++++++++++++++++++++++--------- rtchallenge/src/patterns.rs | 2 +- rtchallenge/src/world.rs | 7 ++- 8 files changed, 96 insertions(+), 45 deletions(-) diff --git a/rtchallenge/examples/eoc6.rs b/rtchallenge/examples/eoc6.rs index 41cbba6..5b14e66 100644 --- a/rtchallenge/examples/eoc6.rs +++ b/rtchallenge/examples/eoc6.rs @@ -22,7 +22,7 @@ fn main() -> Result<()> { let half = wall_size / 2.; let mut shape = Shape::sphere(); shape.material = Material { - color: Color::new(1., 0.2, 1.), + color: Color::new(1., 0.2, 1.).into(), specular: 0.5, diffuse: 0.7, shininess: 30., diff --git a/rtchallenge/examples/eoc7.rs b/rtchallenge/examples/eoc7.rs index 54118d9..5b813ce 100644 --- a/rtchallenge/examples/eoc7.rs +++ b/rtchallenge/examples/eoc7.rs @@ -42,7 +42,7 @@ fn main() -> Result<()> { let mut floor = Shape::sphere(); floor.set_transform(Matrix4x4::scaling(10., 0.01, 10.)); floor.material = Material { - color: Color::new(1., 0.9, 0.9), + color: Color::new(1., 0.9, 0.9).into(), specular: 0., ..Material::default() }; @@ -68,7 +68,7 @@ fn main() -> Result<()> { let mut middle = Shape::sphere(); middle.set_transform(Matrix4x4::translation(-0.5, 1., 0.5)); middle.material = Material { - color: Color::new(0.1, 1., 0.5), + color: Color::new(0.1, 1., 0.5).into(), diffuse: 0.7, specular: 0.3, ..Material::default() @@ -77,7 +77,7 @@ fn main() -> Result<()> { let mut right = Shape::sphere(); right.set_transform(Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5)); right.material = Material { - color: Color::new(0.5, 1., 0.1), + color: Color::new(0.5, 1., 0.1).into(), diffuse: 0.7, specular: 0.3, ..Material::default() @@ -88,7 +88,7 @@ fn main() -> Result<()> { Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33), ); left.material = Material { - color: Color::new(1., 0.8, 0.1), + color: Color::new(1., 0.8, 0.1).into(), diffuse: 0.7, specular: 0.3, ..Material::default() diff --git a/rtchallenge/examples/eoc8.rs b/rtchallenge/examples/eoc8.rs index 4fde11d..6ef89d2 100644 --- a/rtchallenge/examples/eoc8.rs +++ b/rtchallenge/examples/eoc8.rs @@ -54,7 +54,7 @@ fn main() -> Result<()> { let mut floor = Shape::sphere(); floor.set_transform(Matrix4x4::scaling(10., 0.01, 10.)); floor.material = Material { - color: Color::new(1., 0.9, 0.9), + color: Color::new(1., 0.9, 0.9).into(), specular: 0., ..Material::default() }; @@ -80,7 +80,7 @@ fn main() -> Result<()> { let mut middle = Shape::sphere(); middle.set_transform(Matrix4x4::translation(-0.5, 1., 0.5)); middle.material = Material { - color: Color::new(0.1, 1., 0.5), + color: Color::new(0.1, 1., 0.5).into(), diffuse: 0.7, specular: 0.3, ..Material::default() @@ -89,7 +89,7 @@ fn main() -> Result<()> { let mut right = Shape::sphere(); right.set_transform(Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5)); right.material = Material { - color: Color::new(1., 1., 1.), + color: Color::new(1., 1., 1.).into(), diffuse: 0.7, specular: 0.0, ..Material::default() @@ -100,7 +100,7 @@ fn main() -> Result<()> { Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33), ); left.material = Material { - color: Color::new(1., 0.8, 0.1), + color: Color::new(1., 0.8, 0.1).into(), diffuse: 0.7, specular: 0.3, ..Material::default() diff --git a/rtchallenge/examples/eoc9.rs b/rtchallenge/examples/eoc9.rs index b551b5a..2c83822 100644 --- a/rtchallenge/examples/eoc9.rs +++ b/rtchallenge/examples/eoc9.rs @@ -59,14 +59,14 @@ fn main() -> Result<()> { let mut floor = Shape::plane(); floor.material = Material { - color: Color::new(1., 0.2, 0.2), + color: Color::new(1., 0.2, 0.2).into(), specular: 0., ..Material::default() }; let mut ceiling = Shape::plane(); ceiling.set_transform(Matrix4x4::translation(0., 6., 0.) * Matrix4x4::rotation_x(PI)); ceiling.material = Material { - color: Color::new(0.6, 0.6, 0.8), + color: Color::new(0.6, 0.6, 0.8).into(), specular: 0.2, ..Material::default() }; @@ -74,7 +74,7 @@ fn main() -> Result<()> { let mut middle = Shape::sphere(); middle.set_transform(Matrix4x4::translation(-0.5, 0.5, 0.5)); middle.material = Material { - color: Color::new(0.1, 1., 0.5), + color: Color::new(0.1, 1., 0.5).into(), diffuse: 0.7, specular: 0.3, ..Material::default() @@ -83,7 +83,7 @@ fn main() -> Result<()> { let mut right = Shape::sphere(); right.set_transform(Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5)); right.material = Material { - color: Color::new(1., 1., 1.), + color: Color::new(1., 1., 1.).into(), diffuse: 0.7, specular: 0.0, ..Material::default() @@ -94,7 +94,7 @@ fn main() -> Result<()> { Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33), ); left.material = Material { - color: Color::new(1., 0.8, 0.1), + color: Color::new(1., 0.8, 0.1).into(), diffuse: 0.7, specular: 0.3, ..Material::default() diff --git a/rtchallenge/src/lib.rs b/rtchallenge/src/lib.rs index d6fb53e..63be81c 100644 --- a/rtchallenge/src/lib.rs +++ b/rtchallenge/src/lib.rs @@ -45,6 +45,7 @@ pub mod prelude { lights::{PointLight, PointLightBuilder}, materials::{Material, MaterialBuilder}, matrices::{identity, rotation_x, rotation_y, rotation_z, scaling, shearing, translation}, + patterns::stripe_pattern, shapes::{plane, sphere, test_shape}, transformations::view_transform, tuples::{point, vector, Color}, diff --git a/rtchallenge/src/materials.rs b/rtchallenge/src/materials.rs index cc29add..1b2c3c0 100644 --- a/rtchallenge/src/materials.rs +++ b/rtchallenge/src/materials.rs @@ -2,16 +2,43 @@ use derive_builder::Builder; use crate::{ lights::PointLight, - tuples::Color, - tuples::{dot, reflect, Tuple}, + patterns::StripePattern, + 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 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 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: Color, + pub color: ColorMapper, pub ambient: Float, pub diffuse: Float, pub specular: Float, @@ -29,7 +56,7 @@ impl Default for Material { /// assert_eq!( /// m, /// Material { - /// color: WHITE, + /// color: WHITE.into(), /// ambient: 0.1, /// diffuse: 0.9, /// specular: 0.9, @@ -39,7 +66,7 @@ impl Default for Material { /// ``` fn default() -> Material { Material { - color: WHITE, + color: WHITE.into(), ambient: 0.1, diffuse: 0.9, specular: 0.9, @@ -55,56 +82,74 @@ impl Default for Material { /// use rtchallenge::{ /// lights::PointLight, /// materials::{lighting, Material}, -/// tuples::{Color, Tuple}, -/// Float, WHITE, +/// patterns::stripe_pattern, +/// tuples::{point, vector, Color}, +/// Float, BLACK, WHITE, /// }; /// /// let in_shadow = false; /// let m = Material::default(); -/// let position = Tuple::point(0., 0., 0.); +/// let position = point(0., 0., 0.); /// /// // Lighting with the eye between the light and the surface. -/// let eyev = Tuple::vector(0., 0., -1.); -/// let normalv = Tuple::vector(0., 0., -1.); -/// let light = PointLight::new(Tuple::point(0., 0., -10.), WHITE); +/// 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); /// 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 = Tuple::vector(0., (2. as Float).sqrt() / 2., -(2. as Float).sqrt() / 2.); -/// let normalv = Tuple::vector(0., 0., -1.); -/// let light = PointLight::new(Tuple::point(0., 0., -10.), WHITE); +/// 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); /// assert_eq!(result, WHITE); /// /// // Lighting with the eye opposite surface, light offset 45°. -/// let eyev = Tuple::vector(0., 0., -1.); -/// let normalv = Tuple::vector(0., 0., -1.); -/// let light = PointLight::new(Tuple::point(0., 10., -10.), WHITE); +/// 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); /// assert_eq!(result, Color::new(0.7364, 0.7364, 0.7364)); /// /// // Lighting with the eye in the path of the reflection vector. -/// let eyev = Tuple::vector(0., -(2.0 as Float).sqrt() / 2., -(2.0 as Float).sqrt() / 2.); -/// let normalv = Tuple::vector(0., 0., -1.); -/// let light = PointLight::new(Tuple::point(0., 10., -10.), WHITE); +/// 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); /// assert_eq!(result, Color::new(1.63639, 1.63639, 1.63639)); /// /// // Lighting with the light behind the surface. -/// let eyev = Tuple::vector(0., 0., -1.); -/// let normalv = Tuple::vector(0., 0., -1.); -/// let light = PointLight::new(Tuple::point(0., 0., 10.), WHITE); +/// 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); /// assert_eq!(result, Color::new(0.1, 0.1, 0.1)); /// /// // Lighting with the surface in shadow. /// let in_shadow = true; -/// let eyev = Tuple::vector(0., 0., -1.); -/// let normalv = Tuple::vector(0., 0., -1.); -/// let light = PointLight::new(Tuple::point(0., 0., -10.), WHITE); +/// 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); /// assert_eq!(result, Color::new(0.1, 0.1, 0.1)); +/// +/// // Lighting with a pattern applied. +/// +/// let m = Material { +/// color: stripe_pattern(WHITE, BLACK).into(), +/// ambient: 1., +/// diffuse: 0., +/// specular: 0., +/// ..Material::default() +/// }; +/// 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); +/// assert_eq!(c1, WHITE); +/// assert_eq!(c2, BLACK); /// ``` pub fn lighting( material: &Material, @@ -115,7 +160,11 @@ pub fn lighting( in_shadow: bool, ) -> Color { // Combine the surface color with the light's color. - let effective_color = material.color * light.intensity; + let color = match &material.color { + ColorMapper::Constant(color) => *color, + ColorMapper::StripePattern(pat) => pat.stripe_at(point), + }; + let effective_color = color * light.intensity; // Find the direciton of the light source. let lightv = (light.position - point).normalize(); // Compute the ambient distribution. diff --git a/rtchallenge/src/patterns.rs b/rtchallenge/src/patterns.rs index ff10686..78e0b06 100644 --- a/rtchallenge/src/patterns.rs +++ b/rtchallenge/src/patterns.rs @@ -1,6 +1,6 @@ use crate::tuples::{Color, Tuple}; -#[derive(Debug)] +#[derive(Debug, PartialEq, Clone)] pub struct StripePattern { pub a: Color, pub b: Color, diff --git a/rtchallenge/src/world.rs b/rtchallenge/src/world.rs index 5db015e..470dde7 100644 --- a/rtchallenge/src/world.rs +++ b/rtchallenge/src/world.rs @@ -44,7 +44,7 @@ impl World { let light = PointLight::new(Tuple::point(-10., 10., -10.), WHITE); let mut s1 = Shape::sphere(); s1.material = Material { - color: Color::new(0.8, 1., 0.6), + color: [0.8, 1., 0.6].into(), diffuse: 0.7, specular: 0.2, ..Material::default() @@ -161,7 +161,7 @@ impl World { /// shapes::Shape, /// tuples::{Color, Tuple}, /// world::World, - /// BLACK, + /// BLACK, WHITE, /// }; /// /// // The color when a ray misses. @@ -188,7 +188,8 @@ impl World { /// let inner = &w.objects[1]; /// let r = Ray::new(Tuple::point(0., 0., 0.75), Tuple::vector(0., 0., -1.)); /// let c = w.color_at(&r); - /// assert_eq!(c, inner.material.color); + /// // inner.material.color is WHITE + /// assert_eq!(c, WHITE); /// ``` pub fn color_at(&self, r: &Ray) -> Color { match self.intersect(r).hit() {