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)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Material {
|
pub struct Material {
|
||||||
pub color: Color,
|
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