December, 1995

PhotoRealistic RenderMan 3.6 introduces a new function to Shading Language: cellnoise. This application note describes what this function is good for and how to use it.


The Shading Language noise function is used to introduce randomness in shaders, to add complexity and visual realism. Generally, noise is used for two different purposes. Sometimes, noise is used to generate a continuous, pseudo-random, non-periodic function "field", which can be used to attenuate surface bumps, mottle the color of an object, etc.

Another common usage of the noise function in Shading Language is to generate discrete random deviations. These values are used, for example, to color each brick on a wall differently, access a texture map from a different place on each plank of a floor, color each petal on a flower differently, etc. We use the term "cell" to mean each discrete entity (brick, plank, petal, floor tile, etc.). In these cases, the noise value is usually taken from the center or corner of the cell, so that the deviate remains constant over that cell.

Here is an example of how one might compute a slightly different color for each brick in the wall:

brickrow = floor (t / brickheight);
brickcolumn = floor (s / brickwidth);
brickcolor = mix (color1, color2, noise (brickrow*50+brickcolumn+0.5));

The offset of 0.5 causes the noise function to be sampled between integers, since on the integer lattice its value is always 0.5. So noise(i+0.5) is something pseudo-random.

There are two problems with this scheme: (1) noise is rather expensive to call. Much of the expense is in interpolating (making the noise function continuous), but that isn't really needed if we are just sampling discrete locations and don't need the usual continuity constraints. (2) the distribution of noise values is highly biased toward staying close to 0.5 (see Properties of Noise Functions for more details). For the use of noise we're describing, it's much more likely that we want a uniform distribution over [0,1].

Some people try to get around problem (2) by making a construct like the following:

#define SMALL 0.05
n = noise (brickrow*50+brickcolumn+0.5);
brickcolor = mix (color1, color2, mod (n, SMALL) / SMALL);

This has the effect of "wrapping noise around itself", giving a distribution a little more like what you'd want, but not perfect. But it is even more computation, and you lose more floating point accuracy.

So we decided we need a new shadeop that does what we wanted: to provide a cheaper, non-interpolating, uniformly distributed deviate for each cell. Hence, cellnoise.

Cellnoise Basics

The cellnoise function is syntactically similar to noise. It can take inputs of 1-4 dimensions:

  • cellnoise (float) - 1-D cell noise
  • cellnoise (float, float) - 2-D cell noise
  • cellnoise (point) - 3-D cell noise
  • cellnoise (point, float) - 4-D cell noise

Like noise, cellnoise returns either a float, a point, or a color, depending on its context. You can also explicitly ask for a particular return type (this is strongly recommended to make your shader source unambiguous):

  • f = float cellnoise (...)
  • p = point cellnoise (...)
  • c = color cellnoise (...)

Cellnoise Behavior

Cellnoise gets its name because it divides n-D space into cells. The return value of cellnoise is constant over its cell, discontinuous at cell borders, and uncorrelated between cells.

More formally, for all integers i, cellnoise is constant on [i,i+1) and discontinuous at i-eps. Its range values are uniformly distributed on [0,1].

Cellnoise is considerably cheaper to call than noise. In terms of the raw function speed, 1-D cellnoise is about 1.25 times faster to compute than 1-D noise, 2-D cellnoise is twice as fast as 2-D noise, 3-D cellnoise is almost 3 times faster than 3-D noise, and 4-D cellnoise is nearly 6 times faster than 4-D noise. In addition, since you save operations of adding 0.5 and possibly multiplies and divides to fix the range distribution, there's even more overhead that is avoided by using cellnoise.

Sample cellnoise usage

Pattern Variation: you can use cellnoise to assign a different color, offset, orientation, etc., to each discrete entity in a collection. Examples include planks in a floor, bricks in a wall, petals in a flower.

brickrow = floor (t / brickheight);
brickcolumn = floor (s / brickwidth);
brickcolor = mix (color1, color2, cellnoise (brickrow, brickcolumn));
brickroughness = cellnoise (brickrow+100, brickcolumn);
/* offset of 100 makes roughness uncorrelated to color */

Generating a random sequence: You can generate a repeatable, pseudo-random sequence by calling cellnoise in a loop:

for (i = 0;  i < size;  i += 1) {
    n = cellnoise(i);
    /* whatever */

If you want multiple random sequences, or different seeds, you can use the 2-D variant:

n = cellnoise(i,seed);