library: light: add SampleLight trait and iterator
This commit is contained in:
parent
c4557adce1
commit
311b7cd03b
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use super::core::LinearColor;
|
use super::core::LinearColor;
|
||||||
use super::{Point, Vector};
|
use super::{Point, Vector};
|
||||||
|
use beevee::ray::Ray;
|
||||||
use nalgebra::Unit;
|
use nalgebra::Unit;
|
||||||
|
|
||||||
/// Represent a light in the scene being rendered.
|
/// Represent a light in the scene being rendered.
|
||||||
|
@ -16,6 +17,26 @@ pub trait SpatialLight: Light {
|
||||||
fn to_source(&self, origin: &Point) -> (Unit<Vector>, f32);
|
fn to_source(&self, origin: &Point) -> (Unit<Vector>, 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;
|
mod ambient_light;
|
||||||
pub use ambient_light::*;
|
pub use ambient_light::*;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{Light, SpatialLight};
|
use super::{Light, SampleLight, SpatialLight};
|
||||||
use crate::core::LinearColor;
|
use crate::core::LinearColor;
|
||||||
use crate::{Point, Vector};
|
use crate::{Point, Vector};
|
||||||
use beevee::ray::Ray;
|
use beevee::ray::Ray;
|
||||||
|
@ -31,36 +31,6 @@ impl PointLight {
|
||||||
pub fn new(position: Point, color: LinearColor) -> Self {
|
pub fn new(position: Point, color: LinearColor) -> Self {
|
||||||
PointLight { position, color }
|
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 <https://mathworld.wolfram.com/SpherePointPicking.html>
|
|
||||||
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 {
|
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 <https://mathworld.wolfram.com/SpherePointPicking.html>
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{Light, SpatialLight};
|
use super::{Light, SampleLight, SpatialLight};
|
||||||
use crate::core::LinearColor;
|
use crate::core::LinearColor;
|
||||||
use crate::{Point, Vector};
|
use crate::{Point, Vector};
|
||||||
use beevee::ray::Ray;
|
use beevee::ray::Ray;
|
||||||
|
@ -47,25 +47,30 @@ impl SpotLight {
|
||||||
color,
|
color,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Uniformly sample a ray from the spot-light in a random direction.
|
impl Light for SpotLight {
|
||||||
///
|
fn illumination(&self, point: &Point) -> LinearColor {
|
||||||
/// # Examles
|
let delt = point - self.position;
|
||||||
///
|
let cos = self.direction.dot(&delt.normalize());
|
||||||
///```
|
if cos >= self.cosine_value {
|
||||||
/// # use pathtracer::light::SpotLight;
|
self.color.clone() / delt.norm_squared()
|
||||||
/// # use pathtracer::core::color::LinearColor;
|
} else {
|
||||||
/// # use pathtracer::{Point, Vector};
|
LinearColor::black()
|
||||||
/// #
|
}
|
||||||
/// let spot_light = SpotLight::degrees_new(
|
}
|
||||||
/// Point::origin(),
|
}
|
||||||
/// Vector::x_axis(),
|
|
||||||
/// 90.,
|
impl SpatialLight for SpotLight {
|
||||||
/// LinearColor::new(1.0, 0.0, 1.0),
|
fn to_source(&self, point: &Point) -> (Unit<Vector>, f32) {
|
||||||
/// );
|
let delt = self.position - point;
|
||||||
/// let sampled = spot_light.sample_ray();
|
let dist = delt.norm();
|
||||||
/// ```
|
(Unit::new_normalize(delt), dist)
|
||||||
pub fn sample_ray(&self) -> Ray {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SampleLight for SpotLight {
|
||||||
|
fn sample_ray(&self) -> Ray {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
// Sample cap at Z-pole uniformly
|
// Sample cap at Z-pole uniformly
|
||||||
// See <https://math.stackexchange.com/questions/56784>
|
// See <https://math.stackexchange.com/questions/56784>
|
||||||
|
@ -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<Vector>, f32) {
|
|
||||||
let delt = self.position - point;
|
|
||||||
let dist = delt.norm();
|
|
||||||
(Unit::new_normalize(delt), dist)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct SerializedSpotLight {
|
struct SerializedSpotLight {
|
||||||
position: Point,
|
position: Point,
|
||||||
|
|
|
@ -87,6 +87,20 @@ impl LightAggregate {
|
||||||
.chain(self.points.iter().map(|l| l as &dyn SpatialLight))
|
.chain(self.points.iter().map(|l| l as &dyn SpatialLight))
|
||||||
.chain(self.spots.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<Item = &dyn SampleLight> {
|
||||||
|
self.spots
|
||||||
|
.iter()
|
||||||
|
.map(|sl| sl as &dyn SampleLight)
|
||||||
|
.chain(self.points.iter().map(|pl| pl as &dyn SampleLight))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LightAggregate {
|
impl Default for LightAggregate {
|
||||||
|
|
Loading…
Reference in a new issue