A dictionary function for the shading language

For the Air 11 release we’ve added new shaders that compute a physical sky environment and sun position based on geographical location and date/time.  We thought it would be nice to be able to enter a location using a simple city name instead of having to look up and remember a set of figures for latitude, longitude, and time zone every time.

We need a function that allows us to look up a set of values based on a name, like looking up a word’s definition in a dictionary.   We can add such a function to Air’s shading language by implementing a dynamic shadeop, or DSO for short, which will be loaded by Air automatically and used to evaluate our new function.

The dictionary DSO adds the following new functions to the shading language:

float dictionary(string dictname, string keyname, float fvalue);
float dictionary(string dictname, string keyname, color cvalue);
float dictionary(string dictname, string keyname, string svalue);

The dictionary function looks for a text file named dictname and tries to load a list of token value pairs, one per line.  The token values should look like

Seattle 47.45 -122.3 -8

The functions return 0 if they succeed.  If an error occurs, the functions return one of the following error codes:

-1:  unable to find or read dictionary file
-2:  key name not found
-3:  unable to convert value to the requested type

Since Air 11 has not yet been released, here are a couple other uses for the dictionary function that you can try with the current Air release.

The first example uses the dictionary function to allow the base surface color to be specified using a name instead of r,g,b values.  The Air distribution includes a list of named colors in $AIRHOME/vizools/colors.txt (used by the Air Space tool).  Here’s the source code for a simple NamedColor shader:

surface NamedColor(
 float Ambient = 1;
 float Diffuse = 1;
 string ColorName = "";
 string ColorList = "$AIRHOME/viztools/colors.txt"
)
{
  color Ct = Cs;
  if (ColorName!="") dictionary(ColorList,ColorName,Ct);
  normal Nf = faceforward (normalize(N),I);
  Oi = Os;
  Ci = Oi * Ct * (Ambient * ambient() + Diffuse * diffuse(Nf));
}

The dictionary function will expand the reference to the $AIRHOME environment variable in the ColorList value.

In a RIB file, we can then specify a named color as:

Surface "NamedColor"
 "string ColorName" "neon pink"

Another application for the dictionary function is to allow simple text files to be used to define “preset” values for a shader’s parameters.  Here’s an extended version of the standard paintedplastic surface shader with an option to use a file with preset values:

surface paintedplasticpreset(
 float Ka = 1;
 float Kd = .5;
 float Ks = .5;
 float roughness = .1;
 color specularcolor = 1;
 string texturename = "";
 string preset = "";
)
{
 float preKa=Ka;
 float preKd=Kd;
 float preKs=Ks;
 float preroughness = roughness;
 color prespecularcolor=specularcolor;
 string pretexturename=texturename;

 if (preset!="") {
  dictionary(preset,"Ka",preKa);
  dictionary(preset,"Kd",preKd);
  dictionary(preset,"Ks",preKs);
  dictionary(preset,"roughness",preroughness);
  dictionary(preset,"specularcolor",prespecularcolor);
  dictionary(preset,"texturename",pretexturename);
 }
 Ci = Cs;
 if (pretexturename != "")     Ci *= color texture(pretexturename);
 normal Nf = faceforward (normalize(N),I);
 Oi = Os;
 Ci = Oi * Ci * (preKa*ambient() + preKd*diffuse(Nf)) + prespecularcolor * preKs*specular(Nf,-normalize(I),preroughness);
}

A sample preset file might look like:

Kd 0.4
Ks 0.6
roughness 0.2
specularcolor = 1 1 0
texturename = grid.tx

The new dictionary shadeop can found in this archive.  Unzip the archive to the AIRHOME directory, and the new shadop will automatically be added to the shaders directory of your Air installation (where Air can find it).  Examples and source code for the shadeop can be found in $AIRHOME/examples/dictionary.

Posted in Shaders | Tagged , | Comments Off on A dictionary function for the shading language

How to render a normal map

A poster to the Rhino newsgroup recently asked for an easy way to render a normal map based on a model in Rhino.  This simple surface shader can be used to render such a tangent-space normal map with Air:

surface RenderTangentNormalMap(
 float ReverseZ = 1;
)
{
 normal NN=normalize(faceforward(N,I));
 if (ReverseZ!=0) setzcomp(NN,-zcomp(NN));
 NN=0.5+0.5*NN;
 Ci = NN;
 Oi = 1;
}

Here’s an archive with the source code and a pre-compiled binary for Air.  Air users can install the shader by unzipping the archive to the shaders directory of their Air installation.

To generate a normal map, first be sure the output image gamma is set to 1 (RhinoAir users can find this control on the Air Display page in the Rhino Options dialog).  Assign the above shader to your objects, then render an orthographic viewport.  You should see an image with colors similar to those in this sample:

To apply the normal map to an object for rendering, assign the VTangentNormalMap shader as the displacement shader for the object, and set the shader’s TextureName parameter to the file name of the previously generated normal map.  Here’s a simple rendering using the above map:

Posted in Shaders | Tagged , , | Comments Off on How to render a normal map

Viewing the rendering mesh

Air renders most geometric primitives as meshes of polygons.  Viewing the rendering mesh can sometimes help diagnose errors or optimize rendering, whether the mesh is a polygon mesh provided as input or the result of tessellating a smooth surface primitive.

You can use Air’s outline capability to view the individual polygons in a mesh:

One way Air detects edges for outlines is based on a toon id value that is assigned to each element.  By default each geometric primitive is assigned a unique id.  Where two primitives abut in the rendered image, the toon id value changes, and Air will draw an outline.

The toon id for an object can be explicitly specified with:

Attribute "toon" "float id" [n]

If the toon id is set to the special value -2, Air automatically assigns a unique id to each face in the rendering mesh.

So to view the rendering mesh, we just need to enable outlines and to set the toon id to -2 for the objects whose mesh we wish to view.  We can do that by placing a few commands in a small text file:

Option "toon" "maxinkwidth" [0.5]
Attribute "toon" "inkwidth" [0.5]
Attribute "toon" "float id" [-2]

Save the above commands to a file named viewmesh.rib.  Then render with:

air -d viewmesh.rib myscene.rib

We can achieve the same result using a couple of Air’s command line options:

air -d -inkw 0.5 -at toon id -2 myscene.rib

Here are a few images showing the effect of adjusting the flatness tessellation criterion for a blobby object (from 8 to 4 to 2).

Posted in Outlines | Tagged , | Comments Off on Viewing the rendering mesh