For a while I’ve wanted to implement colour temperature control into my lighting workflow but I’ve never been able to figure out how it’s calculated. Then I came across this site, which has already mapped out blackbody temperatures to normalised sRGB values.

Using this as a starting point I mapped out the values into a SL function…

color blackbodyfast( float temperature;) { uniform color c[16] = { (1,0.0401,0),(1,0.1718,0),(1,0.293,0.0257),(1,0.4195,0.1119),(1,0.5336,0.2301), (1,0.6354,0.3684),(1,0.7253,0.517),(1,0.8044,0.6685),(1,0.874,0.8179),(1,0.9254,0.9384),(0.929,0.9107,1), (0.8289,0.8527,1),(0.7531,0.8069,1),(0.6941,0.77,1),(0.6402,0.7352,1),(0.6033,0.7106,1) }; float amount = smoothstep ( 1000, 10000, temperature ); color blackbody = spline ( "catmull-rom", amount, c[0], c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8],c[9], c[10],c[11],c[12],c[13],c[14],c[15], c[15]); return blackbody; }

I decided rather than map every temperature value from 1000K to 40000K, I decided just to deal with 1000K to 10000K using the CIE 1964 10 degree Colour Matching Functions – only because of the later date of 1964, I couldn’t see (nor greatly understand) the difference between the colour matching functions. The original function I wrote called * blackbody* used every value of the kelvin scale from 1000K to 10000K, this resulted in an array of 90 values. The modified one above uses every 6th value which brings the array size down to 16 values, in my tests I didn’t notice a speed difference using 90 values, but looking at a comparison of the two functions I couldn’t see enough visual difference to bother using the full 90 steps.

There is a slight peak where the warm and cool colours meet in the 90 step version. It’s a bit more obvious looking at the image in linear light.

Because the values are in sRGB, they need to be converted to Linear before getting used in the shader. The SL used in the main body of my test surface looks something like this…

#include "colour.h" surface blackbody_srf( uniform float temperature = 5600; #pragma annotation temperature "gadgettype=intslider;min=1000;max=10000;step=100;label=Temperature;" ) { color blackbody = blackbodyfast (temperature); blackbody = sRGB_decode(blackbody); Oi = Os; Ci = blackbody * Oi; }

Used in a light shader the output looks something like this…

The only problem now is that 3Delight doesn’t show a preview of light shader or more importantly the colour temperature in the AE settings for my light.

To get around this I decided to implement an expression which changed the colour of the Maya light that my 3Delight shader was attached to. Because MEL doesn’t have a spline function like SL does I had to improvise using animation curves. First up the MEL to create the three curves that I need to create the RGB colour temperature.

$red = `createNode animCurveTU`; $green = `createNode animCurveTU`; $blue = `createNode animCurveTU`; setKeyframe -itt "spline" -ott "spline" -t 1 -v 1 $red ; setKeyframe -itt "spline" -ott "spline" -t 10 -v 1 $red ; setKeyframe -itt "spline" -ott "spline" -t 11 -v 0.929 $red ; setKeyframe -itt "spline" -ott "spline" -t 12 -v 0.8289 $red ; setKeyframe -itt "spline" -ott "spline" -t 13 -v 0.7531 $red ; setKeyframe -itt "spline" -ott "spline" -t 14 -v 0.6941 $red ; setKeyframe -itt "spline" -ott "spline" -t 15 -v 0.6402 $red ; setKeyframe -itt "spline" -ott "spline" -t 16 -v 0.6033 $red ; setKeyframe -itt "spline" -ott "spline" -t 1 -v 0.0401 $green; setKeyframe -itt "spline" -ott "spline" -t 2 -v 0.172 $green; setKeyframe -itt "spline" -ott "spline" -t 3 -v 0.293 $green; setKeyframe -itt "spline" -ott "spline" -t 4 -v 0.4195 $green; setKeyframe -itt "spline" -ott "spline" -t 5 -v 0.5336 $green; setKeyframe -itt "spline" -ott "spline" -t 6 -v 0.6354 $green; setKeyframe -itt "spline" -ott "spline" -t 7 -v 0.7253 $green; setKeyframe -itt "spline" -ott "spline" -t 8 -v 0.8044 $green; setKeyframe -itt "spline" -ott "spline" -t 9 -v 0.874 $green; setKeyframe -itt "spline" -ott "spline" -t 10 -v 0.9254 $green; setKeyframe -itt "spline" -ott "spline" -t 11 -v 0.9107 $green; setKeyframe -itt "spline" -ott "spline" -t 12 -v 0.8527 $green; setKeyframe -itt "spline" -ott "spline" -t 13 -v 0.8069 $green; setKeyframe -itt "spline" -ott "spline" -t 14 -v 0.77 $green; setKeyframe -itt "spline" -ott "spline" -t 15 -v 0.7352 $green; setKeyframe -itt "spline" -ott "spline" -t 16 -v 0.7106 $green; setKeyframe -itt "spline" -ott "spline" -t 2 -v 0 $blue; setKeyframe -itt "spline" -ott "spline" -t 3 -v 0.0257 $blue; setKeyframe -itt "spline" -ott "spline" -t 4 -v 0.1119 $blue; setKeyframe -itt "spline" -ott "spline" -t 5 -v 0.2301 $blue; setKeyframe -itt "spline" -ott "spline" -t 6 -v 0.3684 $blue; setKeyframe -itt "spline" -ott "spline" -t 7 -v 0.517 $blue; setKeyframe -itt "spline" -ott "spline" -t 8 -v 0.6685 $blue; setKeyframe -itt "spline" -ott "spline" -t 9 -v 0.8179 $blue; setKeyframe -itt "spline" -ott "spline" -t 11 -v 1 $blue; rename $red "colourTemperatureRed"; rename $green "colourTemperatureGreen"; rename $blue "colourTemperatureBlue";

Then the next stage was to create an expression which linked the outputted colour temperature to the light colour.

float $r, $g, $b; if (will_point_lgt1.colourType > 0) { $temp = will_point_lgt1.temperature; $amount = `smoothstep 1000 10000 $temp`; $c = 16 * $amount; $r = `getAttr -t $c colourTemperatureRed.output`; $g = `getAttr -t $c colourTemperatureGreen.output`; $b = `getAttr -t $c colourTemperatureBlue.output`; }else{ $r = will_point_lgt1.lightColourR; $g = will_point_lgt1.lightColourG; $b = will_point_lgt1.lightColourB; } point_lgtShape.colorR = $r; point_lgtShape.colorG = $g; point_lgtShape.colorB = $b;