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 =
float amount = smoothstep ( 1000, 10000, temperature );
color blackbody = spline ( "catmull-rom", amount, c,
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…
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.
The OpenEXR format has a number of useful features which are super handy for CG animation and VFX such as saving the image data in either half or full floating point, setting data-windows and adding additional metadata to the the file. 3Delight for Maya allows you to use all these features, but doesn’t cover how to use them in the documentation (at least I couldn’t find mention of it).
In order to use gain access to these features you need you need to add an extra attribute to the render pass called “exrDisplayParameters”. In the example below, the name of my render pass is called “beauty”.
The above sets the compression type to zip and to also tells 3Delight to autocrop the image when it’s rendered. Auto-crop adjusts the bounding box of the data-window (or ROI, region-of-interest) to only contain non-black pixels (I believe it does this based on the alpha channel), this allows Nuke to process the image quicker as it only calculates information within that data-window. See this tutorial on Nuke Bounding Boxes and how to speed up your compositing operations.
The basic syntax of the parameter string is easy enough to understand, the three arguments passed to the -p flag are name, type and value.
-p "[name]" "[type]" "[value]"
You can also add additional metadata to the header of the EXR render. For example you may wish to include things such as
When you create a custom SL shader in 3Delight it’ll create a automatically create a shader which looks like this in Maya. Now the following UI doesn’t look very useful – the names we’ve called our variables vary in how descriptive they are – which isn’t very useful if others are going to be using this shader
This is based off a shader which looks like this the following SL code.
This will create a shader that looks like this. The hint will be displayed either in the Maya status line or as a tool-tip if you hover the cursor over the UI element.
You can place the #pragma lines anywhere in your SL file, to see them you will need to re-compile the shader and then reload the shader inside Maya by right clicking on the shader, selecting “reload shader” and then selecting the shader in either the Assignment Panel or the Outliner.
// decode from sRGB luma to linear light
float sRGB_decode_f(float F)
if(F <= 0.03928)
lin = F/12.92;
lin = pow((F+0.055)/1.055, 2.4);
color sRGB_decode(color C)
setcomp(D, 0, sRGB_decode_f(comp(C,0)));
setcomp(D, 1, sRGB_decode_f(comp(C,1)));
setcomp(D, 2, sRGB_decode_f(comp(C,2)));
The first function sRGB_decode_f does the majority of the work, the second function sRGB_decode uses that in order to operate on an input colour. To use this in SL we would use something along the lines of this. The first line here creates a colour variable with a mid-grey value in sRGB space. The second line converts that colour into a linear colour.
color myColour = (0.5);
myColour = sRGB_decode(myColour);
Quite often in CG when working with multiple passes – you want to do different things depending on what type of pass your rendering – the two more common ways to do this are to either create two different shaders and change the assignment for each pass or to write a shader which is able to figure out what type of pass it is and act appropriately.
For example in a bake pass you typically want to bake out attributes which aren’t dependant on the position or direction of the camera – this includes things like diffuse/ambient shading, subsurface scattering and ambient occlusion data – which can all be baked out and reused on objects. Only when the position and direction of the objects or lights changes does the scene need to be rebaked.
In order to control this in 3Delight you can use the RiMel commands to setup custom RIB commands which can be read by your shaders. The following MEL commands are placed inside a PreWorldMEL attribute on the render pass itself.
Then within the shader I can query what pass I’m currently rendering and do something appropriate for that type of pass.
uniform string passtype = "";
option( "user:pass", passtype );
if (passtype == "bake")
// Do something here
By default 3Delight also exports out the name of the render pass to an attribute called delight_renderpass_name – this is name of your render pass inside Maya. You can query that name using the following RSL. Obviously using this method highly depends on what you call your passes – the following shading code wouldn’t work if the render pass was called anything other than “bake” – for example if you wanted to do multiple bake passes for within the same scene like separating out animated bakes (which require multiple frames) from static bakes (things which don’t move which can be stored in one frame).
string passtype = "";
if (passtype == "bake")
// Do something here