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 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.,

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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},

View File

@ -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<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: 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.

View File

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

View File

@ -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() {