diff --git a/src/random_distributions/mod.rs b/src/random_distributions/mod.rs index b259ca3..be772b2 100644 --- a/src/random_distributions/mod.rs +++ b/src/random_distributions/mod.rs @@ -13,6 +13,9 @@ pub use cosine_weighted_hemisphere::CosineWeightedHemisphere; mod linear_weighted; pub use linear_weighted::LinearWeighted; +mod sky_light_pdf; +pub use sky_light_pdf::SkyLightPdf; + pub trait RandomDistribution { fn value(&self) -> T; fn pdf(&self, value: T) -> f64; diff --git a/src/random_distributions/sky_light_pdf.rs b/src/random_distributions/sky_light_pdf.rs new file mode 100644 index 0000000..c1ac891 --- /dev/null +++ b/src/random_distributions/sky_light_pdf.rs @@ -0,0 +1,65 @@ +use std::f64::consts::PI; + +use rand::distributions::Open01; +use rand::{thread_rng, Rng}; + +use crate::math::Vec3; + +use super::{LinearWeighted, RandomDistribution}; + +pub struct SkyLightPdf { + z_distribution: LinearWeighted, +} + +impl SkyLightPdf { + pub fn new() -> SkyLightPdf { + let z_distribution = LinearWeighted::new(1.0); + SkyLightPdf { z_distribution } + } +} + +impl RandomDistribution for SkyLightPdf { + fn value(&self) -> Vec3 { + let mut rng = thread_rng(); + let phi = rng.sample::(Open01) * 2.0 * PI; + let z = self.z_distribution.value(); + let r = (1.0 - z * z).sqrt(); + Vec3::new(r * phi.cos(), r * phi.sin(), z) + } + + fn pdf(&self, value: Vec3) -> f64 { + let z = value.z(); + if z < 0.0 { + 0.0 + } else { + z / PI + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore] + fn print_values() { + let target = SkyLightPdf::new(); + for _ in 0..1000 { + let value = target.value(); + println!("{}, {}, {}", value.x(), value.y(), value.z()); + } + } + + #[test] + #[ignore] + fn integral_is_near_area() { + let target = SkyLightPdf::new(); + let integral = (0..100000) + .map(|_| target.value()) + .map(|value| 1.0 / target.pdf(value)) + .sum::() + / 100000.0; + println!("Area: {}\nIntegral: {}", 2.0 * PI, integral); + } +}