Implementing and Debugging the Perlin Noise Algorithm

perlin_header2 One of the most visited articles on our site – How to Use Perlin Noise in Your Games – also caused the most problems. The pseudo-code contained an alarming number of bugs (one of the nastier ones is depicted above), which made it difficult to implement. Readers pointed out these in the comments, and so helped to make the pseudo-code progressively more correct. But even so, some operations remained unclear, so that I finally decided to replace the pseudo-code with real and tested code. I really hope that all bugs have now been squashed!

In the spirit of this extermination effort, this article gives some pointers to get a version of the algorithm up and running as quickly as possible. It is an extension of the original Perlin noise article, and refers to the code now presented there.

Common Errors

Some mistakes that are easy to make:

  • Swapping rows and columns, and widths and heights.
  • Confusing floating point values (in the range 0 to 1) with byte values (in the range 0 to 255). One symptom of this is black images.
  • An error in the interpolation.
  • Sampling the wrong points.
  • Inverting octaves (highest for lowest, and so on).

The following tips will help you avoid these errors, and help track them down faster when they occur.

Implementation Tips

1. Use a non-square image to test

One of the easiest mistakes to make is to swap around widths and heights, or x and y coordinates. Using non-square images will help you detect these errors faster.

2. Implement linear interpolation first

Only once you have this working, you can try other interpolation schemes (such as cosine interpolation). This way there is less risk that the interpolation algorithm is wrong and throws you off the track.

3. Save interim images to file

The smooth noise images are a valuable aid in finding bugs. If they don’t look the way they are supposed to, you know something is wrong. (Below I give some typical examples).

When Things Go Wrong

If you get different results from what you expect, here are some things you can try to help you find the error faster:

1. Check your smooth noise images

Here is how they are supposed to look for different octaves and linear interpolation:

smoothNoise0Octave 0 smoothNoise1Octave 1
smoothNoise2Octave 2 smoothNoise3Octave 3
smoothNoise4Octave 4 smoothNoise5 Octave 5
smoothNoise6 Octave 6

Here is how they are supposed to look for different octaves and cosine interpolation:

smoothNoise0 Octave 0 smoothNoise1 Octave 1
smoothNoise2 Octave 2 smoothNoise3 Octave 3
smoothNoise4 Octave 4 smoothNoise5 Octave 5
smoothNoise6Octave 6

2. Check other variables of the smooth noise function by writing them into images

a. Check the blend factors

Modify the smooth noise algorithm to save the vertical and horizontal blend factors to an image, like this:

float[][] GenerateSmoothNoise(float[][] baseNoise, int octave)
{
   ...
 
   for (int i = 0; i < width; i++)
   {
      ...
 
      for (int j = 0; j < height; j++)
      {
         ...
 
         ////////////////////  Changed for debugging
         //smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);
         smoothNoise[i][j] = horizontal_blend;
      }
   }
 
   ////////////////////  Added for debugging
   SaveImage(smoothNoise, "horizontal_blend" + octave + ".png")
 
   return smoothNoise;
}

For different octaves, you should get a series of ramps from black to white.

For vertical_blend:

vertical_blend0 Octave 0 vertical_blend2 Octave 1
vertical_blend2 Octave 2 vertical_blend3 Octave 3
vertical_blend4 Octave 4 vertical_blend5 Octave 5
vertical_blend6 Octave 6

For horizontal_blend:

horizontal_blend0 Octave 0 horizontal_blend1 Octave 1
horizontal_blend2 Octave 2 horizontal_blend3 Octave 3
horizontal_blend4 Octave 4 horizontal_blend5 Octave 5
horizontal_blend6 Octave 6

b. Check your sample points

Save sample points in an image, by modifying the same line as above to this:

////////////////////  Changed for debugging
//smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);
smoothNoise[i][j] = baseNoise[sample_i0][sample_j0];

Do the same for baseNoise[sample_i1][sample_j0] and baseNoise[sample_i0][sample_j1], then compare them (it is only necessary to compare one octave – choose one that is easy to see). They should all be the same, except that they are translated by the sample period, as shown below:

samplei0j05 i0 j0 samplei1j05 i1 j0 (The same as i0 j0, but translated to the left)
samplei0j15 i0 j1 (The same as i0 j0, but translated to the top)

3. Check that your smooth noise is blended correctly

  • Change your base noise to pure white. The resulting image should also be pure white.
  • Change your base noise to pure black. The resulting image should also be pure black.
  • Check your resulting Perlin noise for different persistence values.

Here is how the images look for different persistent values and linear interpolation.

perlin_noise Persistence = 0.1 perlin_noise Persistence = 0.3
perlin_noise Persistence = 0.5 perlin_noise Persistence = 0.7
perlin_noise Persistence = 0.9

Here is how the images look for different persistent values and cosine interpolation.

perlin_noise Persistence = 0.1 perlin_noise Persistence = 0.3
perlin_noise Persistence = 0.5 perlin_noise Persistence = 0.7

perlin_noisePersistence = 0.9

 

If you have any more implementation or debugging tips to share, please let us know in the comments!

  • Josh S.

    b. Check you sample points

    You should change that to “your.”

    • Dev.Mag

      As always, thank you ;)

  • Lidia Martínez

    I suppose the code you corrected is stored where the other original post links, isn´t it? or is it here… i can´t see it.

    • Dev.Mag

      Yup, I updated the code with the original article.

  • Pingback: How to Use Perlin Noise in Your Games » devmag.org.za

  • Pingback: Minecraft-Clone – Part 2 | LearningGeek Blog

  • xoxos

    quality:
    a = (b[i+2] – b[i+1]) – (b[i-1] – b[i]); // bicubic interpolation
    b = (b[i-1] – b[i]) – a;
    c = b[i+1] – b[i-1];
    d = decimal value
    output = ((a * d + b) * d + c) * d + b[i];

    a = (3 * (b[i] – b[i+1]) – b[i-1] + b[i+2]) * 0.5; // hermite interpolation
    b = b[i+1] + b[i+1] + b[i-1] – (5 * b[i] + b[i+2]) * 0.5;
    c = (b[i+1] – b[i-1]) * 0.5;
    d = decimal value
    output = ((a * d + b) * d + c) * d + b[i];

  • Pingback: Unity Learning Resources – Next Numen