Learning how Perlin noise works

I've been learning about Perlin noise by reading articles, and now I want to solidify that learning by implementing it (in HTML5 canvas / Javascript). I'm following these explanations:

Other authoritative sources, which are not freely accessible:

I'm not going to attempt to duplicate the good explanations that the above articles provide. Instead I'm adding a few illustrations that help to visually reinforce understanding of the concepts in slightly different ways, as I validate my own understanding by implementing the parts of the Perlin noise algorithm.

Caveat: Is that noise really Perlin?

I've discovered in several places on the web, including in the writings of respected computer graphics experts, misleading references to "perlin noise" that were not about Perlin noise at all but value noise, often layered into fractal noise. These are not merely inconsequential mistakes of terminology: They are often followed by discussion about the properties of this "perlin noise" that are untrue, because Perlin noise doesn't work the same way as value noise. For example, Perlin noise yields zero at all lattice points (points where the coordinates are all integers), but value noise doesn't. I wrote to one of these experts asking him to clarify why his article referred to value noise as "perlin noise." He corrected his article, but explained that the phrase had become almost genericized, like saying "post-it notes" when referring to another brand of sticky notes. I suppose that's true to some extent, although this expert had even linked to Perlin's 2002 Siggraph paper on improved Perlin noise... hardly a generic reference. In other cases, writers clearly don't realize that there is a difference, or even continue to insist that their version of Perlin noise is right, despite being alerted to the mistake.

Anyway, watch out for discussion of "Perlin noise" that is actually referring to an entirely different animal, or is mixing multiple types of noise without noticing the difference. Perlin noise is based on dot products with gradient vectors (hence the slightly more general category gradient noise). Anything else is not Perlin noise.

How Perlin noise works

1. Consider 2D Perlin noise, i.e. noise = f(x, y), a scalar value for any point in 2D. A grid is laid out where the lines represent integer values of x and y:

For every grid point, we create a random gradient vector. This is a vector (typically of uniform length, e.g. 1) pointing in a random direction from the grid point. The grid points and gradient vectors are shown in purple in the graph above. The vectors are called gradients because the noise function will have a positive slope (i.e. will increase) in the direction of each gradient vector.

For every pixel P that we need to compute noise for, we evaluate a function using the four neighboring grid points Q and their gradient vectors. Here the pixel we're computing is marked as a cyan dot, the four surrounding grid points are marked as purple dots, and their gradient vectors are again shown in purple (following Zucker's colors).

For each neighboring grid point Q, we take the dot product of the gradient G at Q with the difference vector (P - Q). The result of the dot product (with easing) contributes toward the noise value at P. Here the difference vectors (P - Q) are shown in pink:

Here is the raw contribution of one gradient vector G on its four surrounding grid squares, based on its dot product with the difference vector (P - Q) from the grid point Q to each pixel P. A positive value is yellow; negative is blue:

Here is the same contribution, multiplied by an eased dropoff filter (and scaled up for visibility). The dropoff filter, applied for x and y axes, is

where

The above fade function has a zero first derivative at t=0 and t=1, the borders of each grid cell. That makes it look continuous when the Perlin noise result is used as a color or opacity. But if the noise is used for a normal map, for example, you need the 2nd derivative to be zero (and therefore continuous) at the borders in order to avoid unsightly discontinuities. Hence the "improved" fade/dropoff filter (interpolant function) that has zero 2nd derivative at t=0 and t=1:

Here is the noise field with the contributions from different gradients overlapping (summing):

Further directions:


Lars Huttar, Jan. 2017

Comments, questions, feedback? Email me...