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