materials: implement Phong lighting.
This commit is contained in:
parent
385ed70d88
commit
d8e5476806
@ -1,4 +1,8 @@
|
||||
use crate::tuples::Color;
|
||||
use crate::{
|
||||
lights::PointLight,
|
||||
tuples::Color,
|
||||
tuples::{dot, reflect, Tuple},
|
||||
};
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Material {
|
||||
pub color: Color,
|
||||
@ -37,3 +41,92 @@ impl Default for Material {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const BLACK: Color = Color::new(0., 0., 0.);
|
||||
|
||||
/// Compute lighting contributions using the Phong reflection model.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::{
|
||||
/// lights::PointLight,
|
||||
/// materials::{lighting, Material},
|
||||
/// tuples::{Color, Tuple},
|
||||
/// };
|
||||
///
|
||||
/// let m = Material::default();
|
||||
/// let position = Tuple::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.), Color::new(1., 1., 1.));
|
||||
/// let result = lighting(&m, &light, position, eyev, normalv);
|
||||
/// 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_f32.sqrt() / 2., -2_f32.sqrt() / 2.);
|
||||
/// let normalv = Tuple::vector(0., 0., -1.);
|
||||
/// let light = PointLight::new(Tuple::point(0., 0., -10.), Color::new(1., 1., 1.));
|
||||
/// let result = lighting(&m, &light, position, eyev, normalv);
|
||||
/// assert_eq!(result, Color::new(1.0, 1.0, 1.0));
|
||||
///
|
||||
/// // 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.), Color::new(1., 1., 1.));
|
||||
/// let result = lighting(&m, &light, position, eyev, normalv);
|
||||
/// 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_f32.sqrt() / 2., -2_f32.sqrt() / 2.);
|
||||
/// let normalv = Tuple::vector(0., 0., -1.);
|
||||
/// let light = PointLight::new(Tuple::point(0., 10., -10.), Color::new(1., 1., 1.));
|
||||
/// let result = lighting(&m, &light, position, eyev, normalv);
|
||||
/// assert_eq!(result, Color::new(1.6363853, 1.6363853, 1.6363853));
|
||||
///
|
||||
/// // 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.), Color::new(1., 1., 1.));
|
||||
/// let result = lighting(&m, &light, position, eyev, normalv);
|
||||
/// assert_eq!(result, Color::new(0.1, 0.1, 0.1));
|
||||
/// ```
|
||||
pub fn lighting(
|
||||
material: &Material,
|
||||
light: &PointLight,
|
||||
point: Tuple,
|
||||
eyev: Tuple,
|
||||
normalv: Tuple,
|
||||
) -> Color {
|
||||
// Combine the surface color with the light's color.
|
||||
let effective_color = material.color * light.intensity;
|
||||
// Find the direciton of the light source.
|
||||
let lightv = (light.position - point).normalize();
|
||||
// Compute the ambient distribution.
|
||||
let ambient = effective_color * material.ambient;
|
||||
// This is the cosine of the angle between the light vector an the normal
|
||||
// vector. A negative number means the light is on the other side of the
|
||||
// surface.
|
||||
let light_dot_normal = dot(lightv, normalv);
|
||||
let (diffuse, specular) = if light_dot_normal < 0. {
|
||||
(BLACK, BLACK)
|
||||
} else {
|
||||
// Compute the diffuse contribution.
|
||||
let diffuse = effective_color * material.diffuse * light_dot_normal;
|
||||
// This represents the cosine of the angle between the relfection vector
|
||||
// and the eye vector. A negative number means the light reflects away
|
||||
// from the eye.
|
||||
let reflectv = reflect(-lightv, normalv);
|
||||
let reflect_dot_eye = dot(reflectv, eyev);
|
||||
let specular = if reflect_dot_eye < 0. {
|
||||
BLACK
|
||||
} else {
|
||||
// Compute the specular contribution.
|
||||
let factor = reflect_dot_eye.powf(material.shininess);
|
||||
light.intensity * material.specular * factor
|
||||
};
|
||||
(diffuse, specular)
|
||||
};
|
||||
ambient + diffuse + specular
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user