©A.M.P.A.S.® (click here for legal notice)

Noise and Turbulence

In 1997 I received a Technical Achievement Award from the Academy of Motion Picture Arts and Sciences for work I had done on procedural texture. For example, the NYU Torch on the right is made entirely from procedural textures (except for the text along the bottom). The flame, background, and metal and marble handle are not actually 3D models - they are all entirely faked with textures. A hi-res image of a marble vase I made using this technique can be found here. A bunch of other texture images I created can be found here.

I've written up a fun history and how-to site called MAKING NOISE. It includes a tutorial, examples, animations, and interactive applet diagrams, plus a description of a hardware implementation.

I then improved it, and wrote a paper about that. You can play with interactive demos of the improved version here.

You can play with designing noise-based textures yourself with a really nice interactive Java Applet created by Justin Legakis. Also, the interactive fractal planet demo on my home page is made using these techniques.

It seems that my techniques found their way into the various software packages, such as Autodesk Maya, SoftImage, 3D Studio Max, Dynamation, RenderMan, etc., that folks use to make the effects for feature films, which is way cool. Movies look better now, and I guess that makes me a good American.

Here's what the award actually says:

To Ken Perlin for the development of Perlin Noise, a technique used to produce natural appearing textures on computer generated surfaces for motion picture visual effects.

The development of Perlin Noise has allowed computer graphics artists to better represent the complexity of natural phenomena in visual effects for the motion picture industry.

Needless to say, my Mom was very happy.


Go Here to see the source code for my original C implementation of Noise.

If you want to find out more about procedural textures, here are some places I've written about it:

Improving Noise
Computer Graphics, Vol. 35, No. 3.

Live Paint: Painting with Procedural Multiscale Textures
Computer Graphics, Vol. 28, No. 3.

Texturing and Modeling, A Procedural Approach by David Ebert, et al,
AP Professional, Cambridge, 1994. my chapter is entitled: Noise, Hypertexture, Antialiasing and Gesture

Procedural Texture Synthesis,
section in Computer Graphics Encyclopedia, Kodansha Dai-Ichi Shuppan Publishing, Tokyo.
Synopsis of how to use parametrically controlled procedural models to create widely varying visual textures.

Encyclopedia of Computer Graphics
Article on Perceptually Based Textures. Nov 1990, Kodansha LTD., Tokyo.

Hypertexture, with Eric Hoffert,
1989 Computer Graphics (proceedings of ACM SIGGRAPH Conference), Vol. 22, No. 3.

Procedurally generated textures evaluated throughout volumes to synthesize the appearance of highly textural shapes: flame, fluids, eroded materials, fur. An extension of results from {\it Image Synthesizer} paper to shape synthesis.

An Image Synthesizer,
Computer Graphics, Vol. 19, No. 3. (also in Computer Graphics: Image Synthesis, IEEE, Salem, 1988)
Combines five ideas for visual texture synthesis: (i) 3D space as the texture domain, (ii) an intermediate ``point/normal pixel'' format, (iii) allow arbitrary procedural mappings from point/normal pixels to intensity, (iv) a powerful primitive for introducing controllable noise, (v) an interactive language and environment for texture design. Used to create realistic visual textures of: marble, water waves, fire, clouds, oil films.


/* coherent noise function over 1, 2 or 3 dimensions */
/* (copyright Ken Perlin) */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define B 0x100
#define BM 0xff

#define N 0x1000
#define NP 12   /* 2^N */
#define NM 0xfff

static p[B + B + 2];
static float g3[B + B + 2][3];
static float g2[B + B + 2][2];
static float g1[B + B + 2];
static start = 1;

static void init(void);

#define s_curve(t) ( t * t * (3. - 2. * t) )

#define lerp(t, a, b) ( a + t * (b - a) )

#define setup(i,b0,b1,r0,r1)\
	t = vec[i] + N;\
	b0 = ((int)t) & BM;\
	b1 = (b0+1) & BM;\
	r0 = t - (int)t;\
	r1 = r0 - 1.;

double noise1(double arg)
{
	int bx0, bx1;
	float rx0, rx1, sx, t, u, v, vec[1];

	vec[0] = arg;
	if (start) {
		start = 0;
		init();
	}

	setup(0, bx0,bx1, rx0,rx1);

	sx = s_curve(rx0);

	u = rx0 * g1[ p[ bx0 ] ];
	v = rx1 * g1[ p[ bx1 ] ];

	return lerp(sx, u, v);
}

float noise2(float vec[2])
{
	int bx0, bx1, by0, by1, b00, b10, b01, b11;
	float rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
	register i, j;

	if (start) {
		start = 0;
		init();
	}

	setup(0, bx0,bx1, rx0,rx1);
	setup(1, by0,by1, ry0,ry1);

	i = p[ bx0 ];
	j = p[ bx1 ];

	b00 = p[ i + by0 ];
	b10 = p[ j + by0 ];
	b01 = p[ i + by1 ];
	b11 = p[ j + by1 ];

	sx = s_curve(rx0);
	sy = s_curve(ry0);

#define at2(rx,ry) ( rx * q[0] + ry * q[1] )

	q = g2[ b00 ] ; u = at2(rx0,ry0);
	q = g2[ b10 ] ; v = at2(rx1,ry0);
	a = lerp(sx, u, v);

	q = g2[ b01 ] ; u = at2(rx0,ry1);
	q = g2[ b11 ] ; v = at2(rx1,ry1);
	b = lerp(sx, u, v);

	return lerp(sy, a, b);
}

float noise3(float vec[3])
{
	int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
	float rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
	register i, j;

	if (start) {
		start = 0;
		init();
	}

	setup(0, bx0,bx1, rx0,rx1);
	setup(1, by0,by1, ry0,ry1);
	setup(2, bz0,bz1, rz0,rz1);

	i = p[ bx0 ];
	j = p[ bx1 ];

	b00 = p[ i + by0 ];
	b10 = p[ j + by0 ];
	b01 = p[ i + by1 ];
	b11 = p[ j + by1 ];

	t  = s_curve(rx0);
	sy = s_curve(ry0);
	sz = s_curve(rz0);

#define at3(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] )

	q = g3[ b00 + bz0 ] ; u = at3(rx0,ry0,rz0);
	q = g3[ b10 + bz0 ] ; v = at3(rx1,ry0,rz0);
	a = lerp(t, u, v);

	q = g3[ b01 + bz0 ] ; u = at3(rx0,ry1,rz0);
	q = g3[ b11 + bz0 ] ; v = at3(rx1,ry1,rz0);
	b = lerp(t, u, v);

	c = lerp(sy, a, b);

	q = g3[ b00 + bz1 ] ; u = at3(rx0,ry0,rz1);
	q = g3[ b10 + bz1 ] ; v = at3(rx1,ry0,rz1);
	a = lerp(t, u, v);

	q = g3[ b01 + bz1 ] ; u = at3(rx0,ry1,rz1);
	q = g3[ b11 + bz1 ] ; v = at3(rx1,ry1,rz1);
	b = lerp(t, u, v);

	d = lerp(sy, a, b);

	return lerp(sz, c, d);
}

static void normalize2(float v[2])
{
	float s;

	s = sqrt(v[0] * v[0] + v[1] * v[1]);
	v[0] = v[0] / s;
	v[1] = v[1] / s;
}

static void normalize3(float v[3])
{
	float s;

	s = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
	v[0] = v[0] / s;
	v[1] = v[1] / s;
	v[2] = v[2] / s;
}

static void init(void)
{
	int i, j, k;

	for (i = 0 ; i < B ; i++) {
		p[i] = i;

		g1[i] = (float)((random() % (B + B)) - B) / B;

		for (j = 0 ; j < 2 ; j++)
			g2[i][j] = (float)((random() % (B + B)) - B) / B;
		normalize2(g2[i]);

		for (j = 0 ; j < 3 ; j++)
			g3[i][j] = (float)((random() % (B + B)) - B) / B;
		normalize3(g3[i]);
	}

	while (--i) {
		k = p[i];
		p[i] = p[j = random() % B];
		p[j] = k;
	}

	for (i = 0 ; i < B + 2 ; i++) {
		p[B + i] = p[i];
		g1[B + i] = g1[i];
		for (j = 0 ; j < 2 ; j++)
			g2[B + i][j] = g2[i][j];
		for (j = 0 ; j < 3 ; j++)
			g3[B + i][j] = g3[i][j];
	}
}