use crate::core::pbrt::Float; use crate::core::spectrum::{SpectrumTrait, StandardSpectra}; use crate::utils::math::{clamp, lerp}; use std::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; pub const CIE_Y_INTEGRAL: Float = 106.856895; pub const N_SPECTRUM_SAMPLES: usize = 4; pub const LAMBDA_MIN: i32 = 360; pub const LAMBDA_MAX: i32 = 830; #[derive(Debug, Copy, Clone, PartialEq)] #[repr(C)] pub struct SampledSpectrum { pub values: [Float; N_SPECTRUM_SAMPLES], } impl Default for SampledSpectrum { fn default() -> Self { Self { values: [0.0; N_SPECTRUM_SAMPLES], } } } impl SampledSpectrum { pub fn new(c: Float) -> Self { Self { values: [c; N_SPECTRUM_SAMPLES], } } pub fn zero() -> Self { Self::default() } #[inline(always)] pub fn from_fn(cb: F) -> Self where F: FnMut(usize) -> Float, { Self { values: std::array::from_fn(cb), } } pub fn from_array(v: &[Float]) -> Self { assert!(N_SPECTRUM_SAMPLES == v.len()); let mut values = [0.0; N_SPECTRUM_SAMPLES]; for i in 0..N_SPECTRUM_SAMPLES { values[i] = v[i]; } Self { values } } pub fn is_black(&self) -> bool { self.values.iter().all(|&sample| sample == 0.0) } pub fn has_nans(&self) -> bool { self.values.iter().any(|&v| v.is_nan()) } pub fn min_component_value(&self) -> Float { self.values.iter().fold(Float::INFINITY, |a, &b| a.min(b)) } pub fn max_component_value(&self) -> Float { self.values .iter() .fold(Float::NEG_INFINITY, |a, &b| a.max(b)) } pub fn average(&self) -> Float { self.values.iter().sum::() / (N_SPECTRUM_SAMPLES as Float) } pub fn exp(&self) -> SampledSpectrum { let values = self.values.map(|v| v.exp()); let ret = Self { values }; debug_assert!(!ret.has_nans()); ret } pub fn pow_int(&self, mut power: usize) -> Self { let mut result = Self::new(1.0); let mut base = *self; while power > 0 { if power % 2 == 1 { result *= base; } base *= base; power /= 2; } result } pub fn clamp_zero(s: &SampledSpectrum) -> Self { let ret = SampledSpectrum::from_fn(|i| s[i].max(0.)); debug_assert!(!ret.has_nans()); ret } pub fn sqrt(&self) -> Self { let values = self.values.map(|v| v.sqrt()); let ret = SampledSpectrum { values }; debug_assert!(!ret.has_nans()); ret } pub fn clamp(s: &SampledSpectrum, low: Float, high: Float) -> Self { let ret = SampledSpectrum::from_fn(|i| clamp(s[i], low, high)); debug_assert!(!ret.has_nans()); ret } pub fn safe_div(a: &SampledSpectrum, b: &SampledSpectrum) -> SampledSpectrum { SampledSpectrum::from_fn(|i| if b[i] != 0. { a[i] / b[i] } else { 0. }) } pub fn y(&self, lambda: &SampledWavelengths, std: &StandardSpectra) -> Float { let ys = std.y.sample(lambda); let pdf = lambda.pdf(); SampledSpectrum::safe_div(&(ys * *self), &pdf).average() / CIE_Y_INTEGRAL } } impl<'a> IntoIterator for &'a SampledSpectrum { type Item = &'a Float; type IntoIter = std::slice::Iter<'a, Float>; fn into_iter(self) -> Self::IntoIter { self.values.iter() } } impl<'a> IntoIterator for &'a mut SampledSpectrum { type Item = &'a mut Float; type IntoIter = std::slice::IterMut<'a, Float>; fn into_iter(self) -> Self::IntoIter { self.values.iter_mut() } } impl Index for SampledSpectrum { type Output = Float; fn index(&self, i: usize) -> &Self::Output { &self.values[i] } } impl IndexMut for SampledSpectrum { fn index_mut(&mut self, i: usize) -> &mut Self::Output { &mut self.values[i] } } impl Add for SampledSpectrum { type Output = Self; fn add(self, rhs: Self) -> Self { let mut ret = self; for i in 0..N_SPECTRUM_SAMPLES { ret.values[i] += rhs.values[i]; } ret } } impl AddAssign for SampledSpectrum { fn add_assign(&mut self, rhs: Self) { for i in 0..N_SPECTRUM_SAMPLES { self.values[i] += rhs.values[i]; } } } impl Sub for SampledSpectrum { type Output = Self; fn sub(self, rhs: Self) -> Self { let mut ret = self; for i in 0..N_SPECTRUM_SAMPLES { ret.values[i] -= rhs.values[i]; } ret } } impl SubAssign for SampledSpectrum { fn sub_assign(&mut self, rhs: Self) { for i in 0..N_SPECTRUM_SAMPLES { self.values[i] -= rhs.values[i]; } } } impl Sub for Float { type Output = SampledSpectrum; fn sub(self, rhs: SampledSpectrum) -> SampledSpectrum { SampledSpectrum::from_fn(|i| self - rhs[i]) } } impl Mul for SampledSpectrum { type Output = Self; fn mul(self, rhs: Self) -> Self { let mut ret = self; for i in 0..N_SPECTRUM_SAMPLES { ret.values[i] *= rhs.values[i]; } ret } } impl MulAssign for SampledSpectrum { fn mul_assign(&mut self, rhs: Self) { for i in 0..N_SPECTRUM_SAMPLES { self.values[i] *= rhs.values[i]; } } } impl Mul for SampledSpectrum { type Output = Self; fn mul(self, rhs: Float) -> Self { let mut ret = self; for i in 0..N_SPECTRUM_SAMPLES { ret.values[i] *= rhs; } ret } } impl Mul for Float { type Output = SampledSpectrum; fn mul(self, rhs: SampledSpectrum) -> SampledSpectrum { rhs * self } } impl MulAssign for SampledSpectrum { fn mul_assign(&mut self, rhs: Float) { for i in 0..N_SPECTRUM_SAMPLES { self.values[i] *= rhs; } } } impl DivAssign for SampledSpectrum { fn div_assign(&mut self, rhs: Self) { for i in 0..N_SPECTRUM_SAMPLES { debug_assert_ne!(0.0, rhs.values[i]); self.values[i] /= rhs.values[i]; } } } impl Div for SampledSpectrum { type Output = Self; fn div(self, rhs: Self) -> Self::Output { let mut ret = self; ret /= rhs; ret } } impl Div for SampledSpectrum { type Output = Self; fn div(self, rhs: Float) -> Self::Output { debug_assert_ne!(rhs, 0.0); let mut ret = self; for i in 0..N_SPECTRUM_SAMPLES { ret.values[i] /= rhs; } ret } } impl DivAssign for SampledSpectrum { fn div_assign(&mut self, rhs: Float) { debug_assert_ne!(rhs, 0.0); for i in 0..N_SPECTRUM_SAMPLES { self.values[i] /= rhs; } } } impl Neg for SampledSpectrum { type Output = Self; fn neg(self) -> Self::Output { let mut ret = SampledSpectrum::new(0.0); for i in 0..N_SPECTRUM_SAMPLES { ret.values[i] = -self.values[i]; } ret } } #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq)] pub struct SampledWavelengths { pub lambda: [Float; N_SPECTRUM_SAMPLES], pub pdf: [Float; N_SPECTRUM_SAMPLES], } impl SampledWavelengths { pub fn pdf(&self) -> SampledSpectrum { SampledSpectrum::from_array(&self.pdf) } pub fn secondary_terminated(&self) -> bool { for i in 1..N_SPECTRUM_SAMPLES { if self.pdf[i] != 0.0 { return false; } } true } pub fn terminate_secondary(self) -> Self { if self.secondary_terminated() { return self; } let mut new_pdf = [0.0; N_SPECTRUM_SAMPLES]; new_pdf[0] = self.pdf[0] / (N_SPECTRUM_SAMPLES as Float); Self { pdf: new_pdf, ..self } } pub fn terminate_secondary_inplace(&mut self) {} pub fn sample_uniform(u: Float, lambda_min: Float, lambda_max: Float) -> Self { let mut lambda = [0.0; N_SPECTRUM_SAMPLES]; lambda[0] = lerp(u, lambda_min, lambda_min); let delta = (lambda_max - lambda_min) / N_SPECTRUM_SAMPLES as Float; for i in 1..N_SPECTRUM_SAMPLES { lambda[i] = lambda[i - 1] + delta; if lambda[i] > lambda_max { lambda[i] = lambda_min + (lambda[i] - lambda_max); } } let pdf = [1. / (lambda_max - lambda_min); N_SPECTRUM_SAMPLES]; Self { lambda, pdf } } pub fn sample_visible_wavelengths(u: Float) -> Float { 538.0 - 138.888889 * Float::atanh(0.85691062 - 1.82750197 * u) } pub fn visible_wavelengths_pdf(lambda: Float) -> Float { if !(360.0..830.0).contains(&lambda) { return 0.0; } 0.0039398042 / (Float::cosh(0.0072 * (lambda - 538.0))).sqrt() } pub fn sample_visible(u: Float) -> Self { let mut lambda = [0.0; N_SPECTRUM_SAMPLES]; let mut pdf = [0.0; N_SPECTRUM_SAMPLES]; for i in 0..N_SPECTRUM_SAMPLES { let mut up = u + i as Float / N_SPECTRUM_SAMPLES as Float; if up > 1.0 { up -= 1.0; } lambda[i] = Self::sample_visible_wavelengths(up); pdf[i] = Self::visible_wavelengths_pdf(lambda[i]); } Self { lambda, pdf } } } impl Index for SampledWavelengths { type Output = Float; fn index(&self, i: usize) -> &Self::Output { &self.lambda[i] } } impl IndexMut for SampledWavelengths { fn index_mut(&mut self, i: usize) -> &mut Self::Output { &mut self.lambda[i] } }