Added more code for handling XYZ color and spectral samples.
This commit is contained in:
parent
61e3d47f56
commit
e2954ba5b0
143
src/color/mod.rs
143
src/color/mod.rs
|
@ -2,6 +2,147 @@ mod spectra_xyz;
|
||||||
|
|
||||||
use self::spectra_xyz::{spectrum_xyz_to_p, EQUAL_ENERGY_REFLECTANCE};
|
use self::spectra_xyz::{spectrum_xyz_to_p, EQUAL_ENERGY_REFLECTANCE};
|
||||||
|
|
||||||
pub fn xyz_to_spectrum(xyz: (f32, f32, f32), wavelength: f32) -> f32 {
|
// Minimum and maximum wavelength of light we care about, in nanometers
|
||||||
|
const WL_MIN: f32 = 380.0;
|
||||||
|
const WL_MAX: f32 = 700.0;
|
||||||
|
const WL_RANGE: f32 = WL_MAX - WL_MIN;
|
||||||
|
const WL_RANGE_Q: f32 = WL_RANGE / 4.0;
|
||||||
|
|
||||||
|
pub trait Color {
|
||||||
|
fn sample_spectrum(&self, wavelength: f32) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct SpectralSample {
|
||||||
|
e: [f32; 4],
|
||||||
|
hero_wavelength: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpectralSample {
|
||||||
|
pub fn new(wavelength: f32) -> SpectralSample {
|
||||||
|
debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX);
|
||||||
|
SpectralSample {
|
||||||
|
e: [0.0; 4],
|
||||||
|
hero_wavelength: wavelength,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_color<T: Color>(&mut self, color: &T) {
|
||||||
|
self.e[0] += color.sample_spectrum(self.wl_n(0));
|
||||||
|
self.e[1] += color.sample_spectrum(self.wl_n(1));
|
||||||
|
self.e[2] += color.sample_spectrum(self.wl_n(2));
|
||||||
|
self.e[3] += color.sample_spectrum(self.wl_n(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul_color<T: Color>(&mut self, color: &T) {
|
||||||
|
self.e[0] *= color.sample_spectrum(self.wl_n(0));
|
||||||
|
self.e[1] *= color.sample_spectrum(self.wl_n(1));
|
||||||
|
self.e[2] *= color.sample_spectrum(self.wl_n(2));
|
||||||
|
self.e[3] *= color.sample_spectrum(self.wl_n(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wl_n(&self, n: usize) -> f32 {
|
||||||
|
let mut wl = self.hero_wavelength + (WL_RANGE_Q * n as f32);
|
||||||
|
if wl > WL_MAX {
|
||||||
|
wl - WL_RANGE
|
||||||
|
} else {
|
||||||
|
wl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct XYZ {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XYZ {
|
||||||
|
fn new(x: f32, y: f32, z: f32) -> XYZ {
|
||||||
|
XYZ { x: x, y: y, z: z }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_wavelength(wavelength: f32, intensity: f32) -> XYZ {
|
||||||
|
XYZ {
|
||||||
|
x: x_1931(wavelength) * intensity,
|
||||||
|
y: y_1931(wavelength) * intensity,
|
||||||
|
z: z_1931(wavelength) * intensity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color for XYZ {
|
||||||
|
fn sample_spectrum(&self, wavelength: f32) -> f32 {
|
||||||
|
xyz_to_spectrum((self.x, self.y, self.z), wavelength)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Samples an CIE 1931 XYZ color at a particular wavelength, according to
|
||||||
|
/// the method in the paper "Physically Meaningful Rendering using Tristimulus
|
||||||
|
/// Colours" by Meng et al.
|
||||||
|
fn xyz_to_spectrum(xyz: (f32, f32, f32), wavelength: f32) -> f32 {
|
||||||
spectrum_xyz_to_p(wavelength, xyz) * (1.0 / EQUAL_ENERGY_REFLECTANCE)
|
spectrum_xyz_to_p(wavelength, xyz) * (1.0 / EQUAL_ENERGY_REFLECTANCE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Close analytic approximations of the CIE 1931 XYZ color curves.
|
||||||
|
/// From the paper "Simple Analytic Approximations to the CIE XYZ Color Matching
|
||||||
|
/// Functions" by Wyman et al.
|
||||||
|
fn x_1931(wavelength: f32) -> f32 {
|
||||||
|
let t1 = (wavelength - 442.0) *
|
||||||
|
(if wavelength < 442.0 {
|
||||||
|
0.0624
|
||||||
|
} else {
|
||||||
|
0.0374
|
||||||
|
});
|
||||||
|
let t2 = (wavelength - 599.8) *
|
||||||
|
(if wavelength < 599.8 {
|
||||||
|
0.0264
|
||||||
|
} else {
|
||||||
|
0.0323
|
||||||
|
});
|
||||||
|
let t3 = (wavelength - 501.1) *
|
||||||
|
(if wavelength < 501.1 {
|
||||||
|
0.0490
|
||||||
|
} else {
|
||||||
|
0.0382
|
||||||
|
});
|
||||||
|
(0.362 * (-0.5 * t1 * t1).exp()) + (1.056 * (-0.5 * t2 * t2).exp()) -
|
||||||
|
(0.065 * (-0.5 * t3 * t3).exp())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn y_1931(wavelength: f32) -> f32 {
|
||||||
|
let t1 = (wavelength - 568.8) *
|
||||||
|
(if wavelength < 568.8 {
|
||||||
|
0.0213
|
||||||
|
} else {
|
||||||
|
0.0247
|
||||||
|
});
|
||||||
|
let t2 = (wavelength - 530.9) *
|
||||||
|
(if wavelength < 530.9 {
|
||||||
|
0.0613
|
||||||
|
} else {
|
||||||
|
0.0322
|
||||||
|
});
|
||||||
|
(0.821 * (-0.5 * t1 * t1).exp()) + (0.286 * (-0.5 * t2 * t2).exp())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn z_1931(wavelength: f32) -> f32 {
|
||||||
|
let t1 = (wavelength - 437.0) *
|
||||||
|
(if wavelength < 437.0 {
|
||||||
|
0.0845
|
||||||
|
} else {
|
||||||
|
0.0278
|
||||||
|
});
|
||||||
|
let t2 = (wavelength - 459.0) *
|
||||||
|
(if wavelength < 459.0 {
|
||||||
|
0.0385
|
||||||
|
} else {
|
||||||
|
0.0725
|
||||||
|
});
|
||||||
|
(1.217 * (-0.5 * t1 * t1).exp()) + (0.681 * (-0.5 * t2 * t2).exp())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user