Playing around with PxrSeExpr

This got started from a question on the Renderman forums about the difference between ST and UV. Renderman uses UV and ST to differiate between “implicit” and “explicit” texture coordinates. Implicit is automatically defined by the geometric shape and explicit is manually defined by the user.

With polygons and subdivision geometry, Renderman uses ST coordinates for texture mapping (it converts Maya UVs to Renderman STs behind the scenes). But it also automatically assigns UV coordinates to each face on the geometry, you might ask why would you need this?

For fun and profit!


Heat Map

Useful if you ever want to better visualize data images.

Basic idea is to put the data in the red channel, normalize it between zero and one, then make sure the green and blue channel are both set to one and convert the colourspace from HSV to Linear.

Blackpoint and Whitepoint are set to the minimum and maximum samples from the render. Gain is set to 0.666667 so that the range is mapped from Red to Blue, rather than Red through to Red.

Renderman Denoising

A few little tips and tricks for Denoising stuff in Renderman…

Most of this comes from here…, you’ll need to make sure your environment variables are set correct to do this in the shell.

Note: This is using Windows, a lot of it is similar to Linux/Mac, there might be some syntax differences.

For all examples, we have a directory which contains the following image sequences…

RIS_denoised_variance.####.exr (the main render pass)
RIS_denoised_variance_directdiffuse1.####.exr (key light diffuse)
RIS_denoised_variance_directdiffuse2.####.exr (environment light diffuse)
RIS_denoised_variance_directspecular1.####.exr (key light specular)
RIS_denoised_variance_directspecular2.####.exr (environment light specular)

To denoise several frames (0001-0007) of the beauty render using single-frame denoising. Include the { } brackets around the frame numbers, wildcards * don’t seem to work for me in Windows.

denoise RIS_denoised_variance.{0001,0002,0003,0004,0005,0006,0007}.exr

To denoise the same seven frames (0001-0003) using cross-frame denoising.

denoise --crossframe -v variance RIS_denoised_variance.{0001,0002,0003,0004,0005,0006,0007}.exr

To denoise the AOVs as well, only on three frames this time…

denoise --override filterLayersIndependently true -- RIS_denoised_variance.{0001,0002,0003}.exr RIS_denoised_variance_directdiffuse1.{0001,0002,0003}.exr RIS_denoised_variance_directdiffuse2.{0001,0002,0003}.exr RIS_denoised_variance_directspecular1.{0001,0002,0003}.exr RIS_denoised_variance_directspecular2.{0001,0002,0003}.exr


Colour Temperature in Maya

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] = 
	float amount = smoothstep ( 1000, 10000, temperature );
	color blackbody = spline ( "catmull-rom", amount, c[0],
	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.

Blackbody temperature comparison in sRGB. Temperature is mapped to T coordinate.

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.

Blackbody temperature comparison in Linear. Temperature is mapped to T coordinate.

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…

Blackbody temperature. Light intensity is the same throughout. sRGB.

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";
The resulting animation curves.

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`;
	$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;
Previewing the light inside Maya. The Maya-specific settings of this light are ignored in the final render.

OpenEXR and 3Delight

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”.

addAttr -dt "string" -ln exrDisplayParameters beauty;
setAttr "beauty.exrDisplayParameters" -type "string"
"-p \"compression\" \"string\" \"zip\" -p \"autocrop\" \"integer\" \"1\" ";

The string attribute should end up looking like so in the attribute editor…

-p "compression" "string" "zip" -p "autocrop" "integer" "1"

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

  • Project, scene and shot information.
  • Characters or creatures in the shot.
  • Model, texture, animation versions used.
  • Maya scene used to render the shot.
  • Focal length, Fstop, Shutter Angle, Filmback size.

3Delight already includes some metadata already with the EXR, so you don’t need to add information for the following…

  • Near and far clipping planes.
  • WorldToCamera and WorldToNDC matrices. The Nuke Python documentation has info on how you can use this to create cameras in Nuke based of this data.

You can add this metadata using the “exrheader_” prefix and then the name of your attribute. The following will add three metadata attributes called “shutter”, “haperture” and “vaperture”.

-p "exrheader_shutter" "float" "180" -p "exrheader_haperture" "float" "36" -p "exrheader_vaperture" "float" "24"

While the following will add the project name “ussp” and the maya scene name that was used to render the shot…

-p "exrheader_project" "string" "ussp" -p "exrheader_renderscene" "string" "h:/ussp/bes_0001/scenes/"

The easiest way to get information from your scene to this parameter pass is to set up a Pre-Render MEL script in your render-pass along the lines of…

string $sceneName = `file -q -sn`; //Grab the name of the current scene.
string $projectName = `getenv "PROJECT"`; //This assumes you have an environment variable called "PROJECT" with the project name setup already.
string $parameters = "";
$parameters += (" -p \"exrheader_renderScene\" \"string\" \"" +  $sceneName + "\" ");
$parameters += (" -p \"exrheader_projectName\" \"string\"" + $projectName + "\" ");
setAttr ($pass + ".exrDisplayParameters") -type "string" $parameters;

See the 3Delight documentation has more information on what type of metadata you can add  to the EXR.

Custom Shader UI in 3Delight

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.

surface ui_example_srf
	string texmap = "";
	float blur = 0;
	float usebake = 1;
	float numsamples = 16;
	float doRefl = 0;
	color diffuseColour = color (0.5);

3Delight does however provide a method of creating nice looking shader UIs. You can use #pragma annotations in your shader source code to make things nicer.

#pragma annotation texmap "gadgettype=inputfile;label=Texture Map;hint=Texture Map"
#pragma annotation blur "gadgettype=floatslider;label=Blur;min=0;max=1;hint=Blur the Texture Map"
#pragma annotation usebake "gadgettype=checkbox;label=Use Bake;hint=Use Bake"
#pragma annotation numsamples "gadgettype=intslider;min=1;max=256;label=Samples;hint=Number of samples to use."
#pragma annotation doRefl "gadgettype=optionmenu:gather-env:occlusion-env:ptc-env;label=Reflection Method;hint=Reflection Method"
#pragma annotation diffuseColour "gadgettype=colorslider;label=Diffuse Colour;hint=Diffuse Colour."

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.

Procedural Weathering

Based on seeing the VRay dirt shader, I decided to try replicate it in Renderman.

The above image was created using the following function with a couple of additional layers of noise to create the streak effect.

void dirtOcclusion (
	normal NN; vector down; float invmaxdistance, downmaxdistance, invsamples, downsamples;
	output float invocc, downocc;
	extern point P;
	downocc = 1;
	invocc = 1;
	downocc -= occlusion(P, down, downsamples, "coneangle", PI/6, "bias", 0.1, "maxvariation", 0, "hitsides", "front", "maxdist", downmaxdistance, "falloff", 0.001);
	invocc -= occlusion(P, -NN, invsamples, "coneangle", PI/2 , "bias", 0.1, "maxvariation", 0, "maxdist", invmaxdistance, "hitsides", "back", "falloff", 0);