From 7e450d664e1d8ad9d9ac691d974c6f1abefe107f Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sun, 18 Jul 2021 13:08:03 -0700 Subject: [PATCH] world & intersections: handle shadows. --- rtchallenge/src/intersections.rs | 15 +++++++++++++++ rtchallenge/src/world.rs | 21 ++++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/rtchallenge/src/intersections.rs b/rtchallenge/src/intersections.rs index 313a260..49cd5bd 100644 --- a/rtchallenge/src/intersections.rs +++ b/rtchallenge/src/intersections.rs @@ -4,6 +4,7 @@ use crate::{ rays::Ray, spheres::Sphere, tuples::{dot, Tuple}, + EPSILON, }; #[derive(Debug, Clone, PartialEq)] @@ -138,6 +139,7 @@ pub struct PrecomputedData<'i> { pub t: f32, pub object: &'i Sphere, pub point: Tuple, + pub over_point: Tuple, pub eyev: Tuple, pub normalv: Tuple, pub inside: bool, @@ -150,8 +152,10 @@ pub struct PrecomputedData<'i> { /// use rtchallenge::{ /// intersections::{prepare_computations, Intersection, Intersections}, /// rays::Ray, +/// matrices::Matrix4x4, /// spheres::{intersect, Sphere}, /// tuples::Tuple, +/// EPSILON /// }; /// /// // Precomputing the state of an intersection. @@ -182,6 +186,15 @@ pub struct PrecomputedData<'i> { /// assert_eq!(comps.inside, true); //// // Normal would have been (0, 0, 1), but is inverted when inside. /// 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> { let point = r.position(i.t); @@ -192,10 +205,12 @@ pub fn prepare_computations<'i>(i: &'i Intersection, r: &Ray) -> PrecomputedData } else { (false, normalv) }; + let over_point = point + normalv * EPSILON; PrecomputedData { t: i.t, object: i.object, point, + over_point, normalv, inside, eyev, diff --git a/rtchallenge/src/world.rs b/rtchallenge/src/world.rs index b69e3f4..29a73d8 100644 --- a/rtchallenge/src/world.rs +++ b/rtchallenge/src/world.rs @@ -91,7 +91,9 @@ impl World { /// use rtchallenge::{ /// intersections::{prepare_computations, Intersection}, /// lights::PointLight, + /// matrices::Matrix4x4, /// rays::Ray, + /// spheres::Sphere, /// tuples::{Color, Tuple}, /// world::World, /// WHITE, @@ -115,18 +117,31 @@ impl World { /// let comps = prepare_computations(&i, &r); /// let c = w.shade_hit(&comps); /// 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 { // TODO(wathiede): support multiple light sources by iterating over all // the light sources and summing the calls to lighting. - let in_shadow = false; + let shadowed = self.is_shadowed(comps.over_point); lighting( &comps.object.material, &self.light.as_ref().expect("World has no lights"), - comps.point, + comps.over_point, comps.eyev, comps.normalv, - in_shadow, + shadowed, ) } /// Compute color for given ray fired at the world.