Last active
August 16, 2022 19:10
-
-
Save kettle11/0bd2ed519c2337201c06e343855e87fc to your computer and use it in GitHub Desktop.
Snippet illustrating how to convert Kelvin color temperatures to an sRGB color
This file contains hidden or 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
// 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