-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
improvement: better color handling with XYZ and sRGB
- Loading branch information
Showing
26 changed files
with
230 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
//! Color utilities. | ||
pub mod photon; | ||
pub mod rgb; | ||
pub mod xyz; | ||
|
||
pub use photon::*; | ||
pub use rgb::*; | ||
pub use xyz::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
//! The fundamental building blocks of spectral rendering. | ||
use core::{array::from_fn, ops::Range}; | ||
use rand::rngs::SmallRng; | ||
use rand_distr::uniform::SampleRange; | ||
|
||
/// A fundamental light particle. | ||
#[derive(Copy, Clone, Debug)] | ||
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))] | ||
pub struct Photon { | ||
/// Wavelength of the photon | ||
pub wavelength: Wavelength, | ||
// TODO: spin for polarization | ||
// pub spin: bool, | ||
} | ||
|
||
/// Wavelength in nanometers | ||
pub type Wavelength = usize; | ||
|
||
const MIN_WAVELENGTH: Wavelength = 380; | ||
const MAX_WAVELENGTH: Wavelength = 780; | ||
const SPECTRUM: Range<Wavelength> = MIN_WAVELENGTH..MAX_WAVELENGTH; | ||
const SPECTRUM_SIZE: usize = MAX_WAVELENGTH - MIN_WAVELENGTH; | ||
const WAVE_SAMPLE_COUNT: usize = 4; | ||
|
||
/// Return a random wavelength, sampled uniformly from the visible spectrum. | ||
pub fn random_wavelength(rng: &mut SmallRng) -> Wavelength { | ||
SPECTRUM.sample_single(rng) | ||
} | ||
|
||
/// Given a hero wavelength, create additional equidistant wavelengths in the visible spectrum. Returns an array of wavelengths, with the original hero wavelength as the first one. | ||
#[must_use] | ||
pub fn rotate_wavelength(hero: Wavelength) -> [Wavelength; WAVE_SAMPLE_COUNT] { | ||
from_fn(|j| { | ||
(hero - MIN_WAVELENGTH + ((1 + j) / WAVE_SAMPLE_COUNT) * SPECTRUM_SIZE) | ||
% (SPECTRUM_SIZE + MIN_WAVELENGTH) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
//! RGB colorspace utilities. | ||
use crate::Float; | ||
|
||
use super::XYZ_Normalized; | ||
|
||
/// Linear `sRGB` color based on three [Floats](crate::Float) values. | ||
#[derive(Copy, Clone, Debug)] | ||
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))] | ||
#[allow(non_camel_case_types)] | ||
pub struct sRGB_Linear { | ||
/// The red component of the color, as a [Float] | ||
pub r: Float, | ||
/// The green component of the color, as a [Float] | ||
pub g: Float, | ||
/// The blue component of the color, as a [Float] | ||
pub b: Float, | ||
} | ||
|
||
/// Conversion from XYZ (D65) to linear `sRGB` values <https://color.org/chardata/rgb/sRGB.pdf> | ||
impl From<XYZ_Normalized> for sRGB_Linear { | ||
fn from(value: XYZ_Normalized) -> Self { | ||
let XYZ_Normalized { x, y, z } = value; | ||
let r = 3.240_625_5 * x - 1.537_208 * y - 0.498_628_6 * z; | ||
let g = -0.968_930_7 * x + 1.875_756_1 * y + 0.041_517_5 * z; | ||
let b = 0.055_710_1 * x - 0.204_021_1 * y + 1.056_995_9 * z; | ||
|
||
let r = r.clamp(0.0, 1.0); | ||
let g = g.clamp(0.0, 1.0); | ||
let b = b.clamp(0.0, 1.0); | ||
|
||
sRGB_Linear { r, g, b } | ||
} | ||
} | ||
|
||
/// Color component transfer function. | ||
/// Note: Produces `sRGB` digital values with a range 0 to 1, which must then be multiplied by 2^(bit depth) – 1 and quantized. | ||
/// <https://color.org/chardata/rgb/sRGB.pdf> | ||
#[must_use] | ||
pub fn color_component_transfer(c: Float) -> Float { | ||
if c.abs() < 0.003_130_8 { | ||
12.92 * c | ||
} else { | ||
1.055 * c.powf(1.0 / 2.4) - 0.055 | ||
} | ||
} | ||
|
||
/// Gamma-corrected `sRGB` color based on three [Floats](crate::Float) values. | ||
#[derive(Copy, Clone, Debug)] | ||
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))] | ||
#[allow(non_camel_case_types)] | ||
pub struct sRGB { | ||
/// The red component of the color, as a [Float] | ||
pub r: Float, | ||
/// The green component of the color, as a [Float] | ||
pub g: Float, | ||
/// The blue component of the color, as a [Float] | ||
pub b: Float, | ||
} | ||
|
||
impl From<sRGB_Linear> for sRGB { | ||
fn from(value: sRGB_Linear) -> Self { | ||
let sRGB_Linear { r, g, b } = value; | ||
sRGB { | ||
r: color_component_transfer(r), | ||
g: color_component_transfer(g), | ||
b: color_component_transfer(b), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
//! CIE 1931 XYZ colorspace utilities. | ||
use crate::Float; | ||
|
||
use super::Wavelength; | ||
|
||
/// CIE 1931 XYZ Tristimulus color based on three [Floats](crate::Float) values. | ||
#[derive(Copy, Clone, Debug)] | ||
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))] | ||
#[allow(non_camel_case_types)] | ||
pub struct XYZ_Tristimulus { | ||
/// The x component of the color, as a [Float] | ||
pub x: Float, | ||
/// The y component of the color, as a [Float] | ||
pub y: Float, | ||
/// The z component of the color, as a [Float] | ||
pub z: Float, | ||
} | ||
|
||
/// Helper function adapted from <https://en.wikipedia.org/wiki/CIE_1931_color_space#Analytical_approximation> | ||
fn gaussian(x: Float, alpha: Float, mu: Float, sigma1: Float, sigma2: Float) -> Float { | ||
let t = (x - mu) / (if x < mu { sigma1 } else { sigma2 }); | ||
alpha * (-(t * t) / 2.0).exp() | ||
} | ||
|
||
/// Helper function adapted from <https://en.wikipedia.org/wiki/CIE_1931_color_space#Analytical_approximation> | ||
impl From<Wavelength> for XYZ_Tristimulus { | ||
// TODO: precision loss | ||
#[allow(clippy::cast_precision_loss)] | ||
fn from(lambda: Wavelength) -> Self { | ||
// With the wavelength λ measured in nanometers, we then approximate the 1931 color matching functions: | ||
let l: Float = lambda as Float; | ||
let x = 0.0 // for readability of next lines | ||
+ gaussian(l, 1.056, 599.8, 37.9, 31.0) | ||
+ gaussian(l, 0.362, 442.0, 16.0, 26.7) | ||
+ gaussian(l, -0.065, 501.1, 20.4, 26.2); | ||
let y = gaussian(l, 0.821, 568.8, 46.9, 40.5) + gaussian(l, 0.286, 530.9, 16.3, 31.1); | ||
let z = gaussian(l, 1.217, 437.0, 11.8, 36.0) + gaussian(l, 0.681, 459.0, 26.0, 13.8); | ||
|
||
XYZ_Tristimulus { x, y, z } | ||
} | ||
} | ||
|
||
/// CIE 1931 XYZ Normalized color based on three [Floats](crate::Float) values. | ||
#[derive(Copy, Clone, Debug)] | ||
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))] | ||
#[allow(non_camel_case_types)] | ||
pub struct XYZ_Normalized { | ||
/// The x component of the color, as a [Float] | ||
pub x: Float, | ||
/// The y component of the color, as a [Float] | ||
pub y: Float, | ||
/// The z component of the color, as a [Float] | ||
pub z: Float, | ||
} | ||
|
||
/// Tristimulus value normalization. <https://color.org/chardata/rgb/sRGB.pdf> | ||
impl From<XYZ_Tristimulus> for XYZ_Normalized { | ||
fn from(value: XYZ_Tristimulus) -> Self { | ||
let XYZ_Tristimulus { x, y, z } = value; | ||
// TODO: why does this normalization make the image bad? It should be needed? | ||
|
||
// let x_n = (76.04 * (x - 0.1901)) / (80.0 * (76.04 - 0.1901)); | ||
// let y_n = (y - 0.2) / (80.0 - 0.2); | ||
// let z_n = (87.12 * (z - 0.2178)) / (80.0 * (87.12 - 0.2178)); | ||
|
||
// FIXME: normalization not done, but image looks more correct? | ||
let x_n = x; | ||
let y_n = y; | ||
let z_n = z; | ||
|
||
XYZ_Normalized { | ||
x: x_n, | ||
y: y_n, | ||
z: z_n, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.