15.1. Noise Defined
The purpose of this section is not to explain the mathematical underpinnings of noise but to provide enough of an intuitive feel that you can grasp the noise-based OpenGL shaders presented in the chapter and then use the OpenGL Shading Language to create additional noise-based effects. For a deeper understanding of noise functions, consult the references listed at the end of this chapter, especially Texturing and Modeling: A Procedural Approach, Third Edition, by Ebert et al., which contains several significant discussions of noise, including a description by Perlin of his original noise function. In that book, Darwyn Peachey also provides a taxonomy of noise functions called Making Noises. The application of different noise functions and combinations of noise functions are discussed by Ken Musgrave in his section on building procedural planets.
As Perlin describes it, you can think of noise as "seasoning" for graphics. It often helps to add a little noise, but noise all by itself isn't all that appealing. A perfect model looks a little less perfect and, therefore, a little more realistic if some subtle noise effects are applied.
The ideal noise function has some important qualities.
This definition of noise provides an irregular primitive that adds variety or an apparent element of "randomness" to a regular pattern or period. It can be used as part of modeling, rendering, or animation. Its characteristics make it a valuable tool for creating a variety of interesting effects. Algorithms for creating noise functions make various trade-offs in quality and performance, so they meet the preceding criteria with varying degrees of success.
We can construct a simple noise function (called VALUE NOISE by Peachey) by first assigning a pseudorandom number in the range [1,1] to each integer value along the x axis, as shown in Figure 15.1, and then smoothly interpolating between these points, as shown in Figure 15.2. The function is repeatable in that, for a given input value, it always returns the same output value.
Figure 15.1. A discrete 1D noise function
Figure 15.2. A continuous 1D noise function
A key choice to be made in this type of noise function is the method used to interpolate between successive points. Linear interpolation is not good enough, because the resulting noise pattern shows obvious artifacts. A cubic interpolation method is usually used to produce smooth-looking results.
By varying the frequency and the amplitude, you can get a variety of noise functions (see Figure 15.3).
Figure 15.3. Varying the frequency and the amplitude of the noise function
As you can see, the "features" in these functions get smaller and closer together as the frequency increases and the amplitude decreases. When two frequencies are related by a ratio of 2:1, it's called an OCTAVE. Figure 15.3 illustrates five octaves of the 1D noise function. These images of noise don't look all that useful, but by themselves they can provide some interesting characteristics to shaders. If we add the functions at different frequencies (see Figure 15.4), we start to see something that looks even more interesting.
Figure 15.4. Result of summing noise functions of different amplitude and frequency
The result is a function that contains features of various sizes. The larger bumps from the lower-frequency functions provide the overall shape, whereas the smaller bumps from the higher-frequency functions provide detail and interest at a smaller scale. The function that results from summing the noise of consecutive octaves, each at half the amplitude of the previous octave, was called 1/f noise by Perlin, but the terms "fractional Brownian motion" and "fBm" are used more commonly today.
If you sum octaves of noise in a procedural shader, at some point you will begin to add frequencies that cause aliasing artifacts. Algorithms for antialiasing noise functions typically stop adding detail (higher-frequency noise) before this occurs. This is another key aspect of the noise functionit can be faded to the average value at the point at which aliasing artifacts would begin to occur.
The noise function defined by Perlin (PERLIN NOISE) is sometimes called gradient noise. It is defined as a function whose value is 0 at each integer input value, and its shape is created by defining a pseudorandom gradient vector for the function at each of these points. The characteristics of this noise function make it a somewhat better choice, in general, for the effects we're after (see Ebert et al. (2002) for details). It is used for the implementation of the noise function in RenderMan, and it is also intended to be used for implementations of the noise function built into the OpenGL Shading Language.
Lots of other noise functions have been defined, and there are many ways to vary the basic ideas. The examples of Perlin noise shown previously have a frequency multiplier of 2, but it can be useful to use a frequency multiplier, such as 2.21, that is not an integer value. This frequency multiplier is called the LACUNARITY of the function. The word comes from the Latin word lacuna, which means gap. Using a value larger than 2 allows us to build up more "variety" more quickly (e.g., by summing fewer octaves to achieve the same apparent visual complexity). Similarly, it is not necessary to divide the amplitude of each successive octave by 2.
Summed noise functions are the basis for the terrain and features found in the planet-building software package MojoWorld from Pandromeda. In Texturing and Modeling: A Procedural Approach, Ken Musgrave defines a fractal as "a geometrically complex object, the complexity of which arises through the repetition of a given form over a range of scales." The relationship between the change in frequency and the change in amplitude determines the fractal dimension of the resulting function. If we use a noise function as the basis for generating a terrain model, we can take steps to make it behave differently at different locations. For instance, natural terrain has plains, rolling hills, foothills, and mountains. Varying the fractal dimension based on location can create a similar appearancesuch a function is called a MULTIFRACTAL.
You can achieve interesting effects by using different noise functions for different situations or by combining noise functions of different types. It's not that easy to visualize in advance the results of calculations that depend on noise values, so varied experience will be a key ally as you try to achieve the effect you're after.
15.1.1. 2D Noise
Armed with a basic idea of what the noise function looks like in one dimension, we can take a look at two-dimensional noise. Figure 15.5 contains images of 2D Perlin noise at various frequencies mapped into the range [0,1] and displayed as a grayscale image. Each successive image is twice the frequency of the previous one. In each image, the contrast has been enhanced to make the peaks brighter and the valleys darker. In actual use, each subsequent image has an average that is half the previous one and an amplitude that is half the previous one. If we were to print images of the actual values, the images would be much grayer and it would be harder to see what 2D noise really looks like.
Figure 15.5. Basic 2D noise, at frequencies 4, 8, 16, and 32 (contrast enhanced)
As in the 1D case, adding the different frequency functions provides more interesting results (Figure 15.6).
Figure 15.6. Summed noise, at 1, 2, 3, and 4 octaves (contrast enhanced)
The first image in Figure 15.6 is exactly the same as the first image in Figure 15.5. The second image in Figure 15.6 is the sum of the first image in Figure 15.6 plus half of the second image in Figure 15.5 shifted so that its average intensity value is 0. This causes intensity to be increased in some areas and decreased in others. The third image in Figure 15.6 adds the third octave of noise to the first two, and the fourth image in Figure 15.6 adds the fourth octave. The fourth picture is starting to look a little bit like clouds in the sky.
15.1.2. Higher Dimensions of Noise
3D and 4D noise functions are obvious extensions of the 1D and 2D functions. It's a little hard to generate pictures of 3D noise, but the images in Figure 15.5 can be thought of as 2D slices out of a 3D noise function. Neighboring slices have continuity between them.
Often, a higher dimension of noise is used to control the time aspect of the next lower-dimension noise function. For instance, 1D noise can add some wiggle to otherwise straight lines in a drawing. If you have a 2D noise function, one dimension can control the wiggle, and the second dimension can animate the effect (i.e., make the wiggles move in successive frames). Similarly, a 2D noise function can create a 2D cloud pattern, whereas a 3D noise function can generate the 2D cloud pattern and animate it in a realistic way. With a 4D noise function, you can create a 3D object like a planet and use the fourth dimension to watch it evolve in "fits and starts."
15.1.3. Using Noise in OpenGL Shaders
You include noise in an OpenGL shader in three ways:
With today's graphics hardware, option 3 typically gives the best performance, so in this chapter, we look at some shaders based on this approach. In the next chapter, we look at a shader that uses the built-in noise function to animate geometry. The important thing is to realize the usefulness of noise in computer-generated imagery.