@takram/three-atmosphere
Version:
A Three.js and R3F implementation of Precomputed Atmospheric Scattering
60 lines (51 loc) • 1.91 kB
text/typescript
import { Color, Matrix3, Vector3 } from 'three'
import { clamp, saturate } from '@takram/three-geospatial'
const vectorScratch = /*#__PURE__*/ new Vector3()
// prettier-ignore
const XYZToLinearRGB = /*#__PURE__*/ new Matrix3(
3.2404542, -1.5371385, -0.4985314,
-0.9692660, 1.8760108, 0.0415560,
0.0556434, -0.2040259, 1.0572252
)
export function convertTemperatureToLinearSRGBChromaticity(
temperature: number,
result = new Color()
): Color {
// Convert temperature to black body chromaticity
// See: https://google.github.io/filament/Filament.html#lighting/directlighting/lightsparameterization/colortemperature
const T = temperature
const T2 = T ** 2
const u =
(0.860117757 + 1.54118254e-4 * T + 1.28641212e-7 * T2) /
(1 + 8.42420235e-4 * T + 7.08145163e-7 * T2)
const v =
(0.317398726 + 4.22806245e-5 * T + 4.20481691e-8 * T2) /
(1 - 2.89741816e-5 * T + 1.61456053e-7 * T2)
const x = (3 * u) / (2 * u - 8 * v + 4)
const y = (2 * v) / (2 * u - 8 * v + 4)
// Convert chromaticity to XYZ
const Y = 1
const X = y > 0 ? (x * Y) / y : 0
const Z = y > 0 ? ((1 - x - y) * Y) / y : 0
// Convert XYZ to linear sRGB chromaticity
const color = vectorScratch.set(X, Y, Z).applyMatrix3(XYZToLinearRGB)
// XYZ directly converted from spectral locus doesn't fall inside RGB.
color.x = saturate(color.x)
color.y = saturate(color.y)
color.z = saturate(color.z)
return result.setFromVector3(color.normalize())
}
// See: https://en.wikipedia.org/wiki/Color_index
function convertBVIndexToTemperature(bvIndex: number): number {
const bv = clamp(bvIndex, -0.4, 2)
return 4600 * (1 / (0.92 * bv + 1.7) + 1 / (0.92 * bvIndex + 0.62))
}
export function convertBVIndexToLinearSRGBChromaticity(
bvIndex: number,
result = new Color()
): Color {
return convertTemperatureToLinearSRGBChromaticity(
convertBVIndexToTemperature(bvIndex),
result
)
}