materials: add StripePattern as a Material color option.

This commit is contained in:
Bill Thiede 2021-07-24 19:36:32 -07:00
parent 3e383c4dbd
commit bfa3282a37
8 changed files with 96 additions and 45 deletions

View File

@ -22,7 +22,7 @@ fn main() -> Result<()> {
let half = wall_size / 2.; let half = wall_size / 2.;
let mut shape = Shape::sphere(); let mut shape = Shape::sphere();
shape.material = Material { shape.material = Material {
color: Color::new(1., 0.2, 1.), color: Color::new(1., 0.2, 1.).into(),
specular: 0.5, specular: 0.5,
diffuse: 0.7, diffuse: 0.7,
shininess: 30., shininess: 30.,

View File

@ -42,7 +42,7 @@ fn main() -> Result<()> {
let mut floor = Shape::sphere(); let mut floor = Shape::sphere();
floor.set_transform(Matrix4x4::scaling(10., 0.01, 10.)); floor.set_transform(Matrix4x4::scaling(10., 0.01, 10.));
floor.material = Material { floor.material = Material {
color: Color::new(1., 0.9, 0.9), color: Color::new(1., 0.9, 0.9).into(),
specular: 0., specular: 0.,
..Material::default() ..Material::default()
}; };
@ -68,7 +68,7 @@ fn main() -> Result<()> {
let mut middle = Shape::sphere(); let mut middle = Shape::sphere();
middle.set_transform(Matrix4x4::translation(-0.5, 1., 0.5)); middle.set_transform(Matrix4x4::translation(-0.5, 1., 0.5));
middle.material = Material { middle.material = Material {
color: Color::new(0.1, 1., 0.5), color: Color::new(0.1, 1., 0.5).into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.3, specular: 0.3,
..Material::default() ..Material::default()
@ -77,7 +77,7 @@ fn main() -> Result<()> {
let mut right = Shape::sphere(); 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.set_transform(Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5));
right.material = Material { right.material = Material {
color: Color::new(0.5, 1., 0.1), color: Color::new(0.5, 1., 0.1).into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.3, specular: 0.3,
..Material::default() ..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), Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33),
); );
left.material = Material { left.material = Material {
color: Color::new(1., 0.8, 0.1), color: Color::new(1., 0.8, 0.1).into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.3, specular: 0.3,
..Material::default() ..Material::default()

View File

@ -54,7 +54,7 @@ fn main() -> Result<()> {
let mut floor = Shape::sphere(); let mut floor = Shape::sphere();
floor.set_transform(Matrix4x4::scaling(10., 0.01, 10.)); floor.set_transform(Matrix4x4::scaling(10., 0.01, 10.));
floor.material = Material { floor.material = Material {
color: Color::new(1., 0.9, 0.9), color: Color::new(1., 0.9, 0.9).into(),
specular: 0., specular: 0.,
..Material::default() ..Material::default()
}; };
@ -80,7 +80,7 @@ fn main() -> Result<()> {
let mut middle = Shape::sphere(); let mut middle = Shape::sphere();
middle.set_transform(Matrix4x4::translation(-0.5, 1., 0.5)); middle.set_transform(Matrix4x4::translation(-0.5, 1., 0.5));
middle.material = Material { middle.material = Material {
color: Color::new(0.1, 1., 0.5), color: Color::new(0.1, 1., 0.5).into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.3, specular: 0.3,
..Material::default() ..Material::default()
@ -89,7 +89,7 @@ fn main() -> Result<()> {
let mut right = Shape::sphere(); 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.set_transform(Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5));
right.material = Material { right.material = Material {
color: Color::new(1., 1., 1.), color: Color::new(1., 1., 1.).into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.0, specular: 0.0,
..Material::default() ..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), Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33),
); );
left.material = Material { left.material = Material {
color: Color::new(1., 0.8, 0.1), color: Color::new(1., 0.8, 0.1).into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.3, specular: 0.3,
..Material::default() ..Material::default()

View File

@ -59,14 +59,14 @@ fn main() -> Result<()> {
let mut floor = Shape::plane(); let mut floor = Shape::plane();
floor.material = Material { floor.material = Material {
color: Color::new(1., 0.2, 0.2), color: Color::new(1., 0.2, 0.2).into(),
specular: 0., specular: 0.,
..Material::default() ..Material::default()
}; };
let mut ceiling = Shape::plane(); let mut ceiling = Shape::plane();
ceiling.set_transform(Matrix4x4::translation(0., 6., 0.) * Matrix4x4::rotation_x(PI)); ceiling.set_transform(Matrix4x4::translation(0., 6., 0.) * Matrix4x4::rotation_x(PI));
ceiling.material = Material { 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, specular: 0.2,
..Material::default() ..Material::default()
}; };
@ -74,7 +74,7 @@ fn main() -> Result<()> {
let mut middle = Shape::sphere(); let mut middle = Shape::sphere();
middle.set_transform(Matrix4x4::translation(-0.5, 0.5, 0.5)); middle.set_transform(Matrix4x4::translation(-0.5, 0.5, 0.5));
middle.material = Material { middle.material = Material {
color: Color::new(0.1, 1., 0.5), color: Color::new(0.1, 1., 0.5).into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.3, specular: 0.3,
..Material::default() ..Material::default()
@ -83,7 +83,7 @@ fn main() -> Result<()> {
let mut right = Shape::sphere(); 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.set_transform(Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5));
right.material = Material { right.material = Material {
color: Color::new(1., 1., 1.), color: Color::new(1., 1., 1.).into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.0, specular: 0.0,
..Material::default() ..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), Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33),
); );
left.material = Material { left.material = Material {
color: Color::new(1., 0.8, 0.1), color: Color::new(1., 0.8, 0.1).into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.3, specular: 0.3,
..Material::default() ..Material::default()

View File

@ -45,6 +45,7 @@ pub mod prelude {
lights::{PointLight, PointLightBuilder}, lights::{PointLight, PointLightBuilder},
materials::{Material, MaterialBuilder}, materials::{Material, MaterialBuilder},
matrices::{identity, rotation_x, rotation_y, rotation_z, scaling, shearing, translation}, matrices::{identity, rotation_x, rotation_y, rotation_z, scaling, shearing, translation},
patterns::stripe_pattern,
shapes::{plane, sphere, test_shape}, shapes::{plane, sphere, test_shape},
transformations::view_transform, transformations::view_transform,
tuples::{point, vector, Color}, tuples::{point, vector, Color},

View File

@ -2,16 +2,43 @@ use derive_builder::Builder;
use crate::{ use crate::{
lights::PointLight, lights::PointLight,
tuples::Color, patterns::StripePattern,
tuples::{dot, reflect, Tuple}, tuples::{dot, reflect, Color, Tuple},
Float, BLACK, WHITE, 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)] #[derive(Builder, Debug, PartialEq, Clone)]
#[builder(default)] #[builder(default)]
pub struct Material { pub struct Material {
#[builder(setter(into))] #[builder(setter(into))]
pub color: Color, pub color: ColorMapper,
pub ambient: Float, pub ambient: Float,
pub diffuse: Float, pub diffuse: Float,
pub specular: Float, pub specular: Float,
@ -29,7 +56,7 @@ impl Default for Material {
/// assert_eq!( /// assert_eq!(
/// m, /// m,
/// Material { /// Material {
/// color: WHITE, /// color: WHITE.into(),
/// ambient: 0.1, /// ambient: 0.1,
/// diffuse: 0.9, /// diffuse: 0.9,
/// specular: 0.9, /// specular: 0.9,
@ -39,7 +66,7 @@ impl Default for Material {
/// ``` /// ```
fn default() -> Material { fn default() -> Material {
Material { Material {
color: WHITE, color: WHITE.into(),
ambient: 0.1, ambient: 0.1,
diffuse: 0.9, diffuse: 0.9,
specular: 0.9, specular: 0.9,
@ -55,56 +82,74 @@ impl Default for Material {
/// use rtchallenge::{ /// use rtchallenge::{
/// lights::PointLight, /// lights::PointLight,
/// materials::{lighting, Material}, /// materials::{lighting, Material},
/// tuples::{Color, Tuple}, /// patterns::stripe_pattern,
/// Float, WHITE, /// tuples::{point, vector, Color},
/// Float, BLACK, WHITE,
/// }; /// };
/// ///
/// let in_shadow = false; /// let in_shadow = false;
/// let m = Material::default(); /// 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. /// // Lighting with the eye between the light and the surface.
/// let eyev = Tuple::vector(0., 0., -1.); /// let eyev = vector(0., 0., -1.);
/// let normalv = Tuple::vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(Tuple::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, &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 = Tuple::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 = Tuple::vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(Tuple::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, &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 = Tuple::vector(0., 0., -1.); /// let eyev = vector(0., 0., -1.);
/// let normalv = Tuple::vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(Tuple::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, &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 = Tuple::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 = Tuple::vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(Tuple::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, &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 = Tuple::vector(0., 0., -1.); /// let eyev = vector(0., 0., -1.);
/// let normalv = Tuple::vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(Tuple::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, &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.
/// let in_shadow = true; /// let in_shadow = true;
/// let eyev = Tuple::vector(0., 0., -1.); /// let eyev = vector(0., 0., -1.);
/// let normalv = Tuple::vector(0., 0., -1.); /// let normalv = vector(0., 0., -1.);
/// let light = PointLight::new(Tuple::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, &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.
///
/// 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( pub fn lighting(
material: &Material, material: &Material,
@ -115,7 +160,11 @@ pub fn lighting(
in_shadow: bool, in_shadow: bool,
) -> Color { ) -> Color {
// Combine the surface color with the light's 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. // Find the direciton of the light source.
let lightv = (light.position - point).normalize(); let lightv = (light.position - point).normalize();
// Compute the ambient distribution. // Compute the ambient distribution.

View File

@ -1,6 +1,6 @@
use crate::tuples::{Color, Tuple}; use crate::tuples::{Color, Tuple};
#[derive(Debug)] #[derive(Debug, PartialEq, Clone)]
pub struct StripePattern { pub struct StripePattern {
pub a: Color, pub a: Color,
pub b: Color, pub b: Color,

View File

@ -44,7 +44,7 @@ impl World {
let light = PointLight::new(Tuple::point(-10., 10., -10.), WHITE); let light = PointLight::new(Tuple::point(-10., 10., -10.), WHITE);
let mut s1 = Shape::sphere(); let mut s1 = Shape::sphere();
s1.material = Material { s1.material = Material {
color: Color::new(0.8, 1., 0.6), color: [0.8, 1., 0.6].into(),
diffuse: 0.7, diffuse: 0.7,
specular: 0.2, specular: 0.2,
..Material::default() ..Material::default()
@ -161,7 +161,7 @@ impl World {
/// shapes::Shape, /// shapes::Shape,
/// tuples::{Color, Tuple}, /// tuples::{Color, Tuple},
/// world::World, /// world::World,
/// BLACK, /// BLACK, WHITE,
/// }; /// };
/// ///
/// // The color when a ray misses. /// // The color when a ray misses.
@ -188,7 +188,8 @@ impl World {
/// let inner = &w.objects[1]; /// let inner = &w.objects[1];
/// let r = Ray::new(Tuple::point(0., 0., 0.75), Tuple::vector(0., 0., -1.)); /// let r = Ray::new(Tuple::point(0., 0., 0.75), Tuple::vector(0., 0., -1.));
/// let c = w.color_at(&r); /// 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 { pub fn color_at(&self, r: &Ray) -> Color {
match self.intersect(r).hit() { match self.intersect(r).hit() {