world & intersections: handle shadows.

This commit is contained in:
Bill Thiede 2021-07-18 13:08:03 -07:00
parent 09047eb713
commit 7e450d664e
2 changed files with 33 additions and 3 deletions

View File

@ -4,6 +4,7 @@ use crate::{
rays::Ray, rays::Ray,
spheres::Sphere, spheres::Sphere,
tuples::{dot, Tuple}, tuples::{dot, Tuple},
EPSILON,
}; };
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -138,6 +139,7 @@ pub struct PrecomputedData<'i> {
pub t: f32, pub t: f32,
pub object: &'i Sphere, pub object: &'i Sphere,
pub point: Tuple, pub point: Tuple,
pub over_point: Tuple,
pub eyev: Tuple, pub eyev: Tuple,
pub normalv: Tuple, pub normalv: Tuple,
pub inside: bool, pub inside: bool,
@ -150,8 +152,10 @@ pub struct PrecomputedData<'i> {
/// use rtchallenge::{ /// use rtchallenge::{
/// intersections::{prepare_computations, Intersection, Intersections}, /// intersections::{prepare_computations, Intersection, Intersections},
/// rays::Ray, /// rays::Ray,
/// matrices::Matrix4x4,
/// spheres::{intersect, Sphere}, /// spheres::{intersect, Sphere},
/// tuples::Tuple, /// tuples::Tuple,
/// EPSILON
/// }; /// };
/// ///
/// // Precomputing the state of an intersection. /// // Precomputing the state of an intersection.
@ -182,6 +186,15 @@ pub struct PrecomputedData<'i> {
/// assert_eq!(comps.inside, true); /// assert_eq!(comps.inside, true);
//// // Normal would have been (0, 0, 1), but is inverted when inside. //// // Normal would have been (0, 0, 1), but is inverted when inside.
/// assert_eq!(comps.normalv, Tuple::vector(0., 0., -1.)); /// assert_eq!(comps.normalv, Tuple::vector(0., 0., -1.));
///
/// // The hit should offset the point.
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
/// let mut shape = Sphere::default();
/// shape .set_transform(Matrix4x4::translation(0.,0.,1.));
/// let i = Intersection::new(5., &shape);
/// let comps = prepare_computations(&i, &r);
/// assert!(comps.over_point.z< -EPSILON/2.);
/// assert!(comps.point.z>comps.over_point.z);
/// ``` /// ```
pub fn prepare_computations<'i>(i: &'i Intersection, r: &Ray) -> PrecomputedData<'i> { pub fn prepare_computations<'i>(i: &'i Intersection, r: &Ray) -> PrecomputedData<'i> {
let point = r.position(i.t); let point = r.position(i.t);
@ -192,10 +205,12 @@ pub fn prepare_computations<'i>(i: &'i Intersection, r: &Ray) -> PrecomputedData
} else { } else {
(false, normalv) (false, normalv)
}; };
let over_point = point + normalv * EPSILON;
PrecomputedData { PrecomputedData {
t: i.t, t: i.t,
object: i.object, object: i.object,
point, point,
over_point,
normalv, normalv,
inside, inside,
eyev, eyev,

View File

@ -91,7 +91,9 @@ impl World {
/// use rtchallenge::{ /// use rtchallenge::{
/// intersections::{prepare_computations, Intersection}, /// intersections::{prepare_computations, Intersection},
/// lights::PointLight, /// lights::PointLight,
/// matrices::Matrix4x4,
/// rays::Ray, /// rays::Ray,
/// spheres::Sphere,
/// tuples::{Color, Tuple}, /// tuples::{Color, Tuple},
/// world::World, /// world::World,
/// WHITE, /// WHITE,
@ -115,18 +117,31 @@ impl World {
/// let comps = prepare_computations(&i, &r); /// let comps = prepare_computations(&i, &r);
/// let c = w.shade_hit(&comps); /// let c = w.shade_hit(&comps);
/// assert_eq!(c, Color::new(0.90498, 0.90498, 0.90498)); /// assert_eq!(c, Color::new(0.90498, 0.90498, 0.90498));
///
/// // Shading with an intersection in shadow.
/// let mut w = World::default();
/// w.light = Some(PointLight::new(Tuple::point(0., 0., -10.), WHITE));
/// let s1 = Sphere::default();
/// let mut s2 = Sphere::default();
/// s2.set_transform(Matrix4x4::translation(0., 0., 10.));
/// w.objects = vec![s1, s2.clone()];
/// let r = Ray::new(Tuple::point(0., 0., 5.), Tuple::vector(0., 0., 1.));
/// let i = Intersection::new(4., &s2);
/// let comps = prepare_computations(&i, &r);
/// let c = w.shade_hit(&comps);
/// assert_eq!(c, Color::new(0.1, 0.1, 0.1));
/// ``` /// ```
pub fn shade_hit(&self, comps: &PrecomputedData) -> Color { pub fn shade_hit(&self, comps: &PrecomputedData) -> Color {
// TODO(wathiede): support multiple light sources by iterating over all // TODO(wathiede): support multiple light sources by iterating over all
// the light sources and summing the calls to lighting. // the light sources and summing the calls to lighting.
let in_shadow = false; let shadowed = self.is_shadowed(comps.over_point);
lighting( lighting(
&comps.object.material, &comps.object.material,
&self.light.as_ref().expect("World has no lights"), &self.light.as_ref().expect("World has no lights"),
comps.point, comps.over_point,
comps.eyev, comps.eyev,
comps.normalv, comps.normalv,
in_shadow, shadowed,
) )
} }
/// Compute color for given ray fired at the world. /// Compute color for given ray fired at the world.