pbrt/shared/src/spectra/sampled.rs

395 lines
9.7 KiB
Rust

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<F>(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::<Float>() / (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<usize> for SampledSpectrum {
type Output = Float;
fn index(&self, i: usize) -> &Self::Output {
&self.values[i]
}
}
impl IndexMut<usize> 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<SampledSpectrum> 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<Float> 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<SampledSpectrum> for Float {
type Output = SampledSpectrum;
fn mul(self, rhs: SampledSpectrum) -> SampledSpectrum {
rhs * self
}
}
impl MulAssign<Float> 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<Float> 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<Float> 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<usize> for SampledWavelengths {
type Output = Float;
fn index(&self, i: usize) -> &Self::Output {
&self.lambda[i]
}
}
impl IndexMut<usize> for SampledWavelengths {
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
&mut self.lambda[i]
}
}