pbrt/shared/src/core/spectrum.rs

71 lines
1.9 KiB
Rust

use crate::Float;
use crate::core::color::{RGB, XYZ};
use enum_dispatch::enum_dispatch;
pub use crate::spectra::*;
#[enum_dispatch]
pub trait SpectrumTrait: Copy {
fn evaluate(&self, lambda: Float) -> Float;
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
SampledSpectrum::from_fn(|i| self.evaluate(lambda[i]))
}
fn max_value(&self) -> Float;
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct StandardSpectra {
pub x: DenselySampledSpectrum,
pub y: DenselySampledSpectrum,
pub z: DenselySampledSpectrum,
pub d65: DenselySampledSpectrum,
}
unsafe impl Send for StandardSpectra {}
unsafe impl Sync for StandardSpectra {}
#[repr(C)]
#[enum_dispatch(SpectrumTrait)]
#[derive(Debug, Clone, Copy)]
pub enum Spectrum {
Constant(ConstantSpectrum),
Dense(DenselySampledSpectrum),
Piecewise(PiecewiseLinearSpectrum),
Blackbody(BlackbodySpectrum),
RGBAlbedo(RGBAlbedoSpectrum),
RGBIlluminant(RGBIlluminantSpectrum),
RGBUnbounded(RGBUnboundedSpectrum),
}
impl Spectrum {
pub fn std_illuminant_d65() -> Self {
unimplemented!("Use crate::spectra::default_illuminant() on host")
}
pub fn to_xyz(&self, std: &StandardSpectra) -> XYZ {
let x = self.inner_product(&Spectrum::Dense(std.x));
let y = self.inner_product(&Spectrum::Dense(std.y));
let z = self.inner_product(&Spectrum::Dense(std.z));
XYZ::new(x, y, z) / CIE_Y_INTEGRAL
}
pub fn to_rgb(&self, cs: &RGBColorSpace, std: &StandardSpectra) -> RGB {
let xyz = self.to_xyz(std);
cs.to_rgb(xyz)
}
pub fn inner_product(&self, other: &Spectrum) -> Float {
let mut integral = 0.0;
for lambda in LAMBDA_MIN..=LAMBDA_MAX {
let l = lambda as Float;
integral += self.evaluate(l) * other.evaluate(l);
}
integral
}
pub fn is_constant(&self) -> bool {
matches!(self, Spectrum::Constant(_))
}
}