Skip to content

Instantly share code, notes, and snippets.

@kettle11
Last active August 16, 2022 19:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kettle11/0bd2ed519c2337201c06e343855e87fc to your computer and use it in GitHub Desktop.
Save kettle11/0bd2ed519c2337201c06e343855e87fc to your computer and use it in GitHub Desktop.
Snippet illustrating how to convert Kelvin color temperatures to an sRGB color
// CC0 License
// These functions cannot be directly copy-pasted because they use a library called `kmath`
// but it should be easy enough transcribe to a different math library.
fn temperature_to_srgb_planckian_locus(temperature_kelvin: f64) -> kmath::Vector<f64, 3> {
// See here: https://en.wikipedia.org/wiki/Planckian_locus#Approximation
// And here: https://google.github.io/filament/Filament.html#lighting/directlighting/lightsparameterization
let k = temperature_kelvin;
let k2 = k * k;
// Convert to CIE 1960 (UCS)
let u = (0.860117757 + 1.54118254e-4 * k + 1.28641212e-7 * k2)
/ (1.0 + 8.42420235e-4 * k + 7.08145163e-7 * k2);
let v = (0.317398726 + 4.22806245e-5 * k + 4.20481691e-8 * k2)
/ (1.0 - 2.89741816e-5 * k + 1.61456053e-7 * k2);
// Convert to CIE 1931 xyY
let x0 = (3.0 * u) / (2.0 * u - 8.0 * v + 4.0);
let y0 = (2.0 * v) / (2.0 * u - 8.0 * v + 4.0);
// Convert to CIE XYZ
let x1 = x0 / y0;
let z1 = (1.0 - x0 - y0) / y0;
// XYZ to linear sRGB conversion matrix
let xyz_to_srgb: kmath::Matrix<f64, 3, 3> = [
[3.2404542, -0.9692660, 0.0556434],
[-1.5371385, 1.8760108, -0.2040259],
[-0.4985314, 0.0415560, 1.0572252],
]
.into();
let linear_srgb = xyz_to_srgb * kmath::Vector::<f64, 3>::new(x1, 1.0, z1);
// Normalize and clamp to 0.
let normalized = (linear_srgb / linear_srgb.max_component()).max(kmath::Vector::ZERO);
let srgb = normalized.map(|v| {
if *v <= 0.0031308 {
12.92 * *v
} else {
1.055 * v.powf(1.0 / 2.4) - 0.055
}
});
srgb
}
fn standard_illuminant_series_d_temperature_to_srgb(
temperature_kelvin: FType,
) -> kmath::Vector<FType, 3> {
// See here: https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
let k = temperature_kelvin;
let ik = 1.0 / k;
let ik2 = ik * ik;
let x0 = if k <= 7000.0 {
0.244063 + 0.09911e3 * ik + 2.9678e6 * ik2 - 4.6070e9 * ik2 * ik
} else {
0.237040 + 0.24748e3 * ik + 1.9018e6 * ik2 - 2.0064e9 * ik2 * ik
};
let y0 = -3.0 * x0 * x0 + 2.87 * x0 - 0.275;
// Convert to CIE XYZ
let x1 = x0 / y0;
let z1 = (1.0 - x0 - y0) / y0;
let xyz_to_srgb: kmath::Matrix<FType, 3, 3> = [
[3.2404542, -0.9692660, 0.0556434],
[-1.5371385, 1.8760108, -0.2040259],
[-0.4985314, 0.0415560, 1.0572252],
]
.into();
let linear_srgb = xyz_to_srgb * kmath::Vector::<FType, 3>::new(x1, 1.0, z1);
// Normalize and clamp to 0.
let normalized = (linear_srgb / linear_srgb.max_component()).max(kmath::Vector::ZERO);
let srgb = normalized.map(|v| {
if *v <= 0.0031308 {
12.92 * *v
} else {
1.055 * v.powf(1.0 / 2.4) - 0.055
}
});
srgb
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment