Skip to content

Commit 3bb1552

Browse files
authored
Fix blackbody node to support temperatures down to 800K (#2831)
## Summary Fixes #2827 Extends the blackbody node temperature range down to ~800K (the Draper point) from the previous lower limit of 1667K. ## Changes - **GLSL**: Lowered clamp from 1667K to 800K. The Kang et al. (2002) xc polynomial covers 1000K–4000K with the same formula, so the old limit was unnecessarily conservative. Merged the two redundant xc branches into one. - **OSL**: Replaced manual polynomial with OSL's built-in blackbody() + luminance(), which is physically exact with no lower temperature limit. ## Testing Verified the output chromaticity gradient from 800K–6504K is physically correct and all temperatures below 1667K now return distinct colors instead of being clamped to the 1667K value. ```
1 parent fa20cf1 commit 3bb1552

2 files changed

Lines changed: 16 additions & 51 deletions

File tree

libraries/pbrlib/genglsl/mx_blackbody.glsl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@ void mx_blackbody(float temperatureKelvin, out vec3 colorValue)
88
float xc, yc;
99
float t, t2, t3, xc2, xc3;
1010

11-
// if value outside valid range of approximation clamp to accepted temperature range
12-
temperatureKelvin = clamp(temperatureKelvin, 1667.0, 25000.0);
11+
// Clamp to the range supported by the approximation.
12+
// Lower limit is near the Draper point (~798K), the minimum temperature for visible blackbody emission.
13+
// The Kang et al. (2002) xc polynomial is valid from 1000K, and extrapolates acceptably down to ~800K.
14+
temperatureKelvin = clamp(temperatureKelvin, 800.0, 25000.0);
1315

1416
t = 1000.0 / temperatureKelvin;
1517
t2 = t * t;
1618
t3 = t * t * t;
1719

18-
// Cubic spline approximation for Kelvin temperature to sRGB conversion
20+
// Cubic spline approximation for Kelvin temperature to CIE xy chromaticity
1921
// (https://en.wikipedia.org/wiki/Planckian_locus#Approximation)
20-
if (temperatureKelvin < 4000.0) { // 1667K <= temperatureKelvin < 4000K
22+
// Kang et al. (2002): the same xc polynomial covers 1000K–4000K, so the
23+
// old 1667K lower clamp was unnecessarily conservative.
24+
if (temperatureKelvin < 4000.0) { // 800K <= temperatureKelvin < 4000K
2125
xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t + 0.179910;
2226
}
2327
else { // 4000K <= temperatureKelvin <= 25000K
@@ -26,7 +30,7 @@ void mx_blackbody(float temperatureKelvin, out vec3 colorValue)
2630
xc2 = xc * xc;
2731
xc3 = xc * xc * xc;
2832

29-
if (temperatureKelvin < 2222.0) { // 1667K <= temperatureKelvin < 2222K
33+
if (temperatureKelvin < 2222.0) { // 800K <= temperatureKelvin < 2222K
3034
yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683;
3135
}
3236
else if (temperatureKelvin < 4000.0) { // 2222K <= temperatureKelvin < 4000K
Lines changed: 7 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,10 @@
11
void mx_blackbody(float temp, output color color_value)
22
{
3-
float xc, yc;
4-
float t, t2, t3, xc2, xc3;
5-
6-
// if value outside valid range of approximation clamp to accepted temperature range
7-
float temperature = clamp(temp, 1667.0, 25000.0);
8-
9-
t = 1000.0 / temperature;
10-
t2 = t * t;
11-
t3 = t * t * t;
12-
13-
// Cubic spline approximation for Kelvin temperature to sRGB conversion
14-
// (https://en.wikipedia.org/wiki/Planckian_locus#Approximation)
15-
if (temperature < 4000.0) { // 1667K <= temperature < 4000K
16-
xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t + 0.179910;
17-
}
18-
else { // 4000K <= temperature <= 25000K
19-
xc = -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t + 0.240390;
20-
}
21-
xc2 = xc * xc;
22-
xc3 = xc * xc * xc;
23-
24-
if (temperature < 2222.0) { // 1667K <= temperature < 2222K
25-
yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683;
26-
}
27-
else if (temperature < 4000.0) { // 2222K <= temperature < 4000K
28-
yc = -0.9549476 * xc3 - 1.37418593 * xc2 + 2.09137015 * xc - 0.16748867;
29-
}
30-
else { // 4000K <= temperature <= 25000K
31-
yc = 3.0817580 * xc3 - 5.87338670 * xc2 + 3.75112997 * xc - 0.37001483;
32-
}
33-
34-
if (yc <= 0.0) { // avoid division by zero
35-
color_value = color(1.0);
36-
return;
37-
}
38-
39-
vector XYZ = vector(xc / yc, 1.0, (1 - xc - yc) / yc);
40-
41-
/// XYZ to Rec.709 RGB colorspace conversion
42-
matrix XYZ_to_RGB = matrix( 3.2406, -0.9689, 0.0557, 0.0,
43-
-1.5372, 1.8758, -0.2040, 0.0,
44-
-0.4986, 0.0415, 1.0570, 0.0,
45-
0.0, 0.0, 0.0, 1.0);
46-
47-
color_value = transform(XYZ_to_RGB, XYZ);
48-
color_value = max(color_value, vector(0.0));
3+
// Use OSL's built-in blackbody() function, which implements Planck's law
4+
// over the full physically meaningful range (including below 1667K down to
5+
// the Draper point ~800K). Normalize by luminance to return chromaticity
6+
// at unit brightness, matching the GLSL implementation's convention.
7+
color out = blackbody(temp);
8+
float lum = luminance(out);
9+
color_value = (lum > 0.0) ? out / lum : color(1.0);
4910
}

0 commit comments

Comments
 (0)