SiVi -- Simplex Noise Visualizer

I strongly recommend reading Stefan Gustavson's Simplex noise demystified for an explanation of the mathematics of gradient noise. I will be making references to concepts which he explains far better than I could.

I. Basics

My goal is to focus on coding for procedural noise. So, let's go straight to a code sample!

The following Java fragment shows a typical usage of SimplexNoise. It is followed by a graphic of the texture generated.

1:         BufferedImage image = new BufferedImage(width, height,
2:                 BufferedImage.TYPE_INT_RGB);
3:         WritableRaster raster = image.getRaster();
4:         int[] pixel = new int[3]; 
6:         double noiseValue;
8:         for (int y = 0; y < height; y++)
9:        {
10:            for (int x = 0; x < width; x++)
11:            {
12:                noiseValue = SimplexNoise.noise(x / 128f, y / 128f);
13:                noiseValue = (noiseValue + 1) / 2;
14:                noiseValue *= 256;
16:                pixel[0] = (int)noiseValue;  // red
17:                pixel[1] = pixel[0];  // green
18:                pixel[2] = pixel[0];  // blue
20:                raster.setPixel(x, y, pixel);
21:            }
22:        }

Fig. 1 Resulting texture

The main task involves iterating through every pixel of the desired image, making use of the coordinate values of the pixels as parameters in the noise function call. We do this with nested for loops. The noise function call, on line 12, returns a double that has a value within the range (-1, 1) which is stored in the variable noiseValue. On lines 13 and 14, noiseValue is transformed into a number that has a meaningful color value: an integer in the range [0, 255]. In line 13, we add 1 to the noiseValue, which shifts the range to (0, 2), then we divide by 2 to normalize the range to (0, 1). If you are defining your colors via a normalized float, the step on line 14 would be omitted. In lines 16 through 20, the pixel is defined and used to set the corresponding pixel in the graphic.

Note that in this example, the X and Y values are multiplied by factors of 1/128 prior to serving as parameters. The exact factor used will vary depending on the graphical goals, and is directly related to the locations of the anchor points of the random gradients. As Gustavson explains, in 2D, the random gradients are positioned in a triangular gridwork with the base of the triangles being a unit in length. By using a factor of 1/128, it will take 128 iterations, i.e., 128 pixels on the screen, for the X or Y coordinate to progress one unit. Higher or lower factors can be used to make the values change more quickly or more slowly over the range of the graphic, as can be seen in the following diagram.

Fig. 2 Three scalings, from left to right, (a) X interval is 1/128, (b) X interval is 1/256 (twice as small), (c) X interval is 1/64 (twice as large). The Y interval remains 1/128 in all three.

The scaling factor is usually expressed as a variable, e.g.,

    noiseValue = SimplexNoise.noise( x * xScaling, y * yScaling);

When more than one octave of noise is being combined, the most common situation is that each octave has its own set of scaling factors. We will discuss this more in the next tutorial.

The value used as the scaling factor is usually defined either to be relative to the screen resolution or to the size of the graphic. In the first case, the texture always has the same density on the screen, regardless of the size of the graphic. In the second case, the texture's density will track the of size of graphic. To achieve this, the width of the graphic is used in the definition of xScaling and the height is used for yScaling. Scaling can become even more sophisticated, creating directional or even perspective effects, when the value of the scaling factor is itself a function of X or Y or some interesting combination.

Another common operation is to add in a transpositional value to the noise parameters. This could look like the following:

13:    noiseValue = SimplexNoise.noise(
                xTransposition + (x / 128f), 
                yTransposition + (y / 128f) 

Fig. 1 Two translations, (a) xTranslate = 0   (b) xTranslate = 1. The texture has been shifted to the left.

Sometimes, one puts in a translation value to "seed" the texture. This could also be done by adding another dimension and giving that dimension a constant value other than zero. But adding dimensions has a significant cpu cost, and a large, random x or y translation should suffice to guarantee that while the basic aspects of the texture will be similar, the particular details will be unique.

Implementation of scaling and translation in SiVi:

Scaling in SiVi has a built in base factor of 1/256. Scalings that are proportional to the size of the graphic, or are a function of X and/or Y have not been implemented, though I have placed this on the project wish list. X and Y translations are possible, but with a limited granularity. Another project wish list item is to allow X and Y translation to be animation parameters.

If you found this tutorial helpful and wish to show your gratitude, please let me know. Your "thank you" will be greatly appreciated!