diff --git a/pathtracer/src/light/mod.rs b/pathtracer/src/light/mod.rs index ca3bf77..e7e68a5 100644 --- a/pathtracer/src/light/mod.rs +++ b/pathtracer/src/light/mod.rs @@ -2,6 +2,7 @@ use super::core::LinearColor; use super::{Point, Vector}; +use beevee::ray::Ray; use nalgebra::Unit; /// Represent a light in the scene being rendered. @@ -16,6 +17,26 @@ pub trait SpatialLight: Light { fn to_source(&self, origin: &Point) -> (Unit, f32); } +/// Represent a light from which we can sample a random `Ray`. +pub trait SampleLight: Light { + /// Uniformly sample a ray from the point-light in a random direction. + /// + /// # Examles + /// + ///``` + /// # use pathtracer::light::{PointLight, SampleLight}; + /// # use pathtracer::core::color::LinearColor; + /// # use pathtracer::Point; + /// # + /// let dir_light = PointLight::new( + /// Point::origin(), + /// LinearColor::new(1.0, 0.0, 1.0), + /// ); + /// let sampled = dir_light.sample_ray(); + /// ``` + fn sample_ray(&self) -> Ray; +} + mod ambient_light; pub use ambient_light::*; diff --git a/pathtracer/src/light/point_light.rs b/pathtracer/src/light/point_light.rs index 19f9d59..a26863b 100644 --- a/pathtracer/src/light/point_light.rs +++ b/pathtracer/src/light/point_light.rs @@ -1,4 +1,4 @@ -use super::{Light, SpatialLight}; +use super::{Light, SampleLight, SpatialLight}; use crate::core::LinearColor; use crate::{Point, Vector}; use beevee::ray::Ray; @@ -31,36 +31,6 @@ impl PointLight { pub fn new(position: Point, color: LinearColor) -> Self { PointLight { position, color } } - - /// Uniformly sample a ray from the point-light in a random direction. - /// - /// # Examles - /// - ///``` - /// # use pathtracer::light::PointLight; - /// # use pathtracer::core::color::LinearColor; - /// # use pathtracer::Point; - /// # - /// let dir_light = PointLight::new( - /// Point::origin(), - /// LinearColor::new(1.0, 0.0, 1.0), - /// ); - /// let sampled = dir_light.sample_ray(); - /// ``` - pub fn sample_ray(&self) -> Ray { - let mut rng = rand::thread_rng(); - // Sample sphere uniformly - // See - let theta = rng.gen_range(0., std::f32::consts::PI * 2.); - let y = rng.sample(Uniform::new(-1., 1.)); // Inclusive for the poles - let dir = Unit::new_unchecked(Vector::new( - // this vector is already of unit length - f32::sqrt(1. - y * y) * f32::cos(theta), - y, - f32::sqrt(1. - y * y) * f32::sin(theta), - )); - Ray::new(self.position, dir) - } } impl Light for PointLight { @@ -78,6 +48,23 @@ impl SpatialLight for PointLight { } } +impl SampleLight for PointLight { + fn sample_ray(&self) -> Ray { + let mut rng = rand::thread_rng(); + // Sample sphere uniformly + // See + let theta = rng.gen_range(0., std::f32::consts::PI * 2.); + let y = rng.sample(Uniform::new(-1., 1.)); // Inclusive for the poles + let dir = Unit::new_unchecked(Vector::new( + // this vector is already of unit length + f32::sqrt(1. - y * y) * f32::cos(theta), + y, + f32::sqrt(1. - y * y) * f32::sin(theta), + )); + Ray::new(self.position, dir) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/pathtracer/src/light/spot_light.rs b/pathtracer/src/light/spot_light.rs index 220e370..1526071 100644 --- a/pathtracer/src/light/spot_light.rs +++ b/pathtracer/src/light/spot_light.rs @@ -1,4 +1,4 @@ -use super::{Light, SpatialLight}; +use super::{Light, SampleLight, SpatialLight}; use crate::core::LinearColor; use crate::{Point, Vector}; use beevee::ray::Ray; @@ -47,25 +47,30 @@ impl SpotLight { color, ) } +} - /// Uniformly sample a ray from the spot-light in a random direction. - /// - /// # Examles - /// - ///``` - /// # use pathtracer::light::SpotLight; - /// # use pathtracer::core::color::LinearColor; - /// # use pathtracer::{Point, Vector}; - /// # - /// let spot_light = SpotLight::degrees_new( - /// Point::origin(), - /// Vector::x_axis(), - /// 90., - /// LinearColor::new(1.0, 0.0, 1.0), - /// ); - /// let sampled = spot_light.sample_ray(); - /// ``` - pub fn sample_ray(&self) -> Ray { +impl Light for SpotLight { + fn illumination(&self, point: &Point) -> LinearColor { + let delt = point - self.position; + let cos = self.direction.dot(&delt.normalize()); + if cos >= self.cosine_value { + self.color.clone() / delt.norm_squared() + } else { + LinearColor::black() + } + } +} + +impl SpatialLight for SpotLight { + fn to_source(&self, point: &Point) -> (Unit, f32) { + let delt = self.position - point; + let dist = delt.norm(); + (Unit::new_normalize(delt), dist) + } +} + +impl SampleLight for SpotLight { + fn sample_ray(&self) -> Ray { let mut rng = rand::thread_rng(); // Sample cap at Z-pole uniformly // See @@ -94,26 +99,6 @@ impl SpotLight { } } -impl Light for SpotLight { - fn illumination(&self, point: &Point) -> LinearColor { - let delt = point - self.position; - let cos = self.direction.dot(&delt.normalize()); - if cos >= self.cosine_value { - self.color.clone() / delt.norm_squared() - } else { - LinearColor::black() - } - } -} - -impl SpatialLight for SpotLight { - fn to_source(&self, point: &Point) -> (Unit, f32) { - let delt = self.position - point; - let dist = delt.norm(); - (Unit::new_normalize(delt), dist) - } -} - #[derive(Debug, Deserialize)] struct SerializedSpotLight { position: Point, diff --git a/pathtracer/src/scene/light_aggregate.rs b/pathtracer/src/scene/light_aggregate.rs index acd0a82..876d62c 100644 --- a/pathtracer/src/scene/light_aggregate.rs +++ b/pathtracer/src/scene/light_aggregate.rs @@ -87,6 +87,20 @@ impl LightAggregate { .chain(self.points.iter().map(|l| l as &dyn SpatialLight)) .chain(self.spots.iter().map(|l| l as &dyn SpatialLight)) } + + /// Returns an iterator over the aggregate's [`SampleLight`]s. + /// + /// This simply merges iterators over [`SpotLight`], and [`PointLight`]. + /// + /// [`SampleLight`]: ../../light/trait.SampleLight.html + /// [`PointLight`]: ../../light/point_light/struct.PointLight.html + /// [`Spotight`]: ../../light/spot_light/struct.Spotight.html + pub fn sample_lights_iter(&self) -> impl Iterator { + self.spots + .iter() + .map(|sl| sl as &dyn SampleLight) + .chain(self.points.iter().map(|pl| pl as &dyn SampleLight)) + } } impl Default for LightAggregate {