Simplex Noise Demystified
Simplex Noise Demystified
net/publication/216813608
CITATIONS READS
42 11,387
1 author:
Stefan Gustavson
Linköping University
22 PUBLICATIONS 471 CITATIONS
SEE PROFILE
All content following this page was uploaded by Stefan Gustavson on 12 August 2014.
Classic noise
In order to explain simplex noise, it is helpful to have a good understanding of classic Perlin
noise. I have seen quite a few bad and misinformed explanations in this area, so to make sure
that you have the necessary groundwork done, I will present classic Perlin noise first.
Perlin noise is a so-called gradient noise, which means that you set a pseudo-random gradient
at regularly spaced points in space, and interpolate a smooth function between those points. To
generate Perlin noise in one dimension, you associate a pseudo-random gradient (or slope) for
the noise function with each integer coordinate, and set the function value at each integer coor-
dinate to zero.
0 1 2 3 4 5
For a given point x somewhere between two integer points, the value is interpolated between
two values, namely the values that would have been the result if the closest linear slopes from
the left and from the right had been extrapolated to the point in question. This interpolation is
not linear with distance, because that would not satisfy the constraint that the derivative of the
noise function should be continuous also at the integer points. Instead, a blending function is
used that has zero derivative at its endpoints. Originally, Ken Perlin used the Hermite blending
2 3
function f(t) = 3t – 2t , but because it is highly desirable to have a continuous second deriv-
ative for the noise function, he later changed that to suggest a fifth degree polynomial
5 4 3
f(t) = 6t – 15t + 10t . These two functions are very similar, but the fifth degree curve also
has a zero second derivative at its endpoints, which makes the noise function have a continuous
second derivative everywhere, and that makes the noise function better suited for the common
computer graphics tasks of surface displacement and bump mapping.
1
0.9
3t2-2t3
0.8 6t5-15t4+10t3
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
In two dimensions, the integer coordinate points form a regular square grid. At each grid point
a pseudo-random 2D gradient is picked. For an arbitrary point P on the surface, the noise value
is computed from the four closest grid points. As for the 1D case, the contribution from each of
the four corners of the square grid cell is an extrapolation of a linear ramp with a constant gra-
dient, with the value zero at its associated grid point.
y y P
j+1
v
j
u x
x
i i+1
0 1 2 3 4
The value of each gradient ramp is computed by means of a scalar product (dot product) be-
tween the gradient vectors of each grid point and the vectors from the grid points
( i, j ), ( i + 1, j ), ( i, j + 1 ), ( i + 1, j + 1 ) to the point P being evaluated. In equations:
P = ( x, y ) , i = floor(x) , j = floor(y)
g 00 = gradient at ( i, j ) , g 10 = gradient at ( i + 1, j )
g 01 = gradient at ( i, j + 1 ) , g11 = gradient at ( i + 1, j + 1 )
u = x – i, v = y – j
n 00 = g 00 • u , n 10 = g 10 • u – 1 , n 01 = g 01 • u , n 11 = g 11 • u – 1
v v v–1 v–1
The blending of the noise contribution from the four corners is performed in a manner similar
to bilinear interpolation, using the fifth order blending curve to compute the interpolant:
5 4 3
f(t) = 6t – 15t + 10t (1-f()) and f() should be
n x0 = n 00 f(u) + n 10 ( 1 – f(u) ) , n x1 = n01 f(u) + n 11 ( 1 – f(u) ) swapped in these three
n xy = nx0 f(v) + n x1 ( 1 – f(v) ) equations. My bad.
The result n xy is the final value of the noise function for the point ( x, y ) . In 3D, the gradients
are three-dimensional and the interpolation is performed along three axes, one at a time. I do not
present details here, but you can find it in the code examples at the end of this document.
Generalization can be made to an arbitrary number of dimensions.
z g 0 = ( 0, 1, 1 ) , g1 = ( 0, 1, – 1 ) ,
g 2 = ( 0, – 1, 1 ) , g 3 = ( 0, – 1, – 1 ) ,
g 4 = ( 1, 0, 1 ) , g5 = ( 1, 0, – 1 ) ,
y g 6 = ( – 1, 0, 1 ) , g 7 = ( – 1, 0, – 1 ) ,
g 8 = ( 1, 1, 0 ) , g9 = ( 1, – 1, 0 ) ,
x
g 10 = ( – 1, 1, 0 ) , g 11 = ( – 1, – 1, 0 )
For 4D, a suitable set of gradients is the midpoints of each of the 32 edges of a 4D hypercube:
g 0 = ( 0, 1, 1, 1 ) , g 1 = ( 0, 1, 1, – 1 ) , g 2 = ( 0, 1, – 1, 1 ) , g 3 = ( 0, 1, – 1, – 1 ) ,
g 4 = ( 0, – 1, 1, 1 ) , g 5 = ( 0, – 1, 1, – 1 ) , g 6 = ( 0, – 1, – 1, 1 ) , g 7 = ( 0, – 1, – 1, – 1 ) ,
g 8 = ( 1, 0, 1, 1 ) , g 9 = ( 1, 0, 1, – 1 ) , g 10 = ( 1, 0, – 1, 1 ) , g 11 = ( 1, 0, – 1, – 1 ) ,
g 12 = ( – 1, 0, 1, 1 ) , g 13 = ( – 1, 0, 1, – 1 ) , g 14 = ( – 1, 0, – 1, 1 ) , g 15 = ( – 1, 0, – 1, – 1 ) ,
g 16 = ( 1, 1, 0, 1 ) , g 17 = ( 1, 1, 0, – 1 ) , g 18 = ( 1, – 1, 0, 1 ) , g 19 = ( 1, – 1, 0, – 1 ) ,
g 20 = ( – 1, 1, 0, 1 ) , g 21 = ( – 1, 1, 0, – 1 ) , g 22 = ( – 1, – 1, 0, 1 ) , g 23 = ( – 1, – 1, 0, – 1 ) ,
g 24 = ( 1, 1, 1, 0 ) , g 25 = ( 1, 1, – 1, 0 ) , g 26 = ( 1, – 1, 1, 0 ) , g 27 = ( 1, – 1, – 1, 0 ) ,
g 28 = ( – 1, 1, 1, 0 ) , g 29 = ( – 1, 1, – 1, 0 ) , g 30 = ( – 1, – 1, 1, 0 ) , g 31 = ( – 1, – 1, – 1, 0 )
It really doesn’t matter very much exactly which gradients are picked, as long as they are not
too few and are reasonably evenly distributed over all directions. Gradients with values of only
0 , 1 and –1 for each component are chosen because taking a dot product with such a vector
does not require any multiplications, only additions and subtractions.
To associate each grid point with exactly one gradient, the integer coordinates of the point can
be used to compute a hash value, which in turn can be used as the index into a look-up table of
the gradients.
This concludes my presentation of classic Perlin noise.
Simplex grids
Simplex grids, or simplical tesselation of N-space, sounds fancy and is a bit hard to grasp, but
what it boils down to is quite simple: for a space with N dimensions, pick the simplest and most
compact shape that can be repeated to fill the entire space. For a one-dimensional space, the sim-
plest (in fact, the only possible) space-filling shape is intervals of equal length placed one after
another, head to tail. In two dimensions, the obvious choice for a space-filling shape is a square,
but that shape has more corners than what is necessary. The simplest shape that tiles a 2D plane
is a triangle, and the formal simplex shape in 2D is an equilateral triangle. Two of these make a
rhombus, a shape that can be thought of as a square that has been squashed along its main diag-
onal.
In three dimensions, the simplex shape is a slightly skewed tetrahedron, six of which make a
cube that has been squashed along its main diagonal.
y x
z
In four dimensions, the simplex shape is very hard to visualize, but it has five corners, and 24
of these shapes make a 4D hypercube that has been squashed along its main diagonal. Generally
speaking, the simplex shape for N dimensions is a shape with N + 1 corners, and N! of these
shapes can fill an N-dimensional hypercube that has been squashed along its main diagonal.
The definite advantage of a simplex shape is that it has as few corners as possible, much fewer
corners than a hypercube. This makes it easier to interpolate values in the interior of the simplex
N
based on the values at its corners. While a hypercube in N dimensions has 2 corners, a sim-
plex in N dimensions has only N + 1 corners. As we move to higher dimensions, the complex-
ity of evaluating a function at each corner of a hypercube and interpolating along each principal
N
axis is an O ( 2 ) problem that quickly becomes intractable, while a similar evaluation for each
corner of a simplex shape followed by interpolation presents a much less daunting computation-
al task of complexity O ( N ) . N^2, actually.
Moving from interpolation to summation
A fundamental problem of classic noise is that it involves sequential interpolations along each
dimension. Apart from the rapid increase in computational complexity as we move to higher di-
mensions, it becomes more and more of a problem to compute the analytic derivative of the in-
terpolated function. Simplex noise instead uses a straight summation of contributions from each
corner, where the contribution is a multiplication of the extrapolation of the gradient ramp and
a radially symmetric attenuation function. In signal processing terms, this is a signal reconstruc-
tion kernel. The radial attenuation is carefully chosen so that the influence from each corner
reaches zero before crossing the boundary to the next simplex. This means that points inside a
simplex will only be influenced by the contributions from the corners of that particular simplex.
For 2D, the influence from each corner can be visualized as a small wiggle function (Ken Perlin
uses the term “surflet”) around the corner point, with any point on the surface having at most
three non-zero parts of a wiggle covering it.
+ + =
Just like a 2D simplex grid can be skewed to a regular square grid, a 3D simplex grid can be
skewed to a regular cubical grid by a scaling along the main diagonal, and the integer parts of
the coordinates for the transformed point can be used to determine which cell of 6 simplices the
point is in. To further determine which of the 6 simplices we are in, we look at the magnitude
of the coordinates relative to the cell origin. The figure below is a view of a cube cell along its
main diagonal x = y = z . Points in the six simplices obey quite simple rules.
y y>x>z
y>z>x x>y>z
x
z>y>x
x>z>y
z z>x>y
For the 4D case, things become incomprehensible visually, but the methods presented for 2D
and 3D generalize nicely to higher dimensions. Sorting the (x,y,z,w) coordinates within the hy-
percube reveals 4! = 24 possible outcomes for the ordering of the magnitude of the coordi-
nates, and each particular ordering is unique to one of the 24 simplices.
In 2D, if x > y the simplex corners are ( 0, 0 ) , ( 1, 0 ) and ( 1, 1 ) , else the simplex corners are
( 0, 0 ) , ( 0, 1 ) and ( 1, 1 ) . The simplex traversal always takes one unit step in x and one unit
step in y , but in different order for each of the simplices.
The traversal scheme for 2D generalizes straight off to 3D and further to an arbitrary number of
dimensions: to traverse each corner of a simplex in N dimensions, we should move from the
origin of the hypercube ( 0, 0, …, 0 ) to the opposite corner ( 1, 1, …, 1 ) , and move unit steps
along each principal axis in turn, from the coordinate with the highest magnitude to the coordi-
nate with the lowest magnitude.
The magnitude sorting of components to decide which order of unit steps along each dimension
that traverses the simplex can be performed explicitly, by the usual pair-wise comparisons and
swaps, but the results from the comparisons can also be used as an index into a lookup table.
This is efficient and hardware friendly up to 5 dimensions, but becomes unwieldy for 6D and
downright silly for higher dimensions. Explicit sorting will of course add to the complexity.
NOTE: Better public domain code from 2012 is now available at
Example code http://www.itn.liu.se/~stegu/simplexnoise/SimplexNoise.java
All this can be put into code, and the code can be made a lot more clear than Ken Perlin’s com-
pact and uncommented Java reference implementation. I will use a hybrid approach for clarity,
using the gradient hash method from classic noise but the simplex grid and straight summation
of noise contributions of simplex noise. That is actually a faster method in software. My imple-
mentation gives the same visual appearance as Perlins simplex noise, but it does not give exactly
the same pattern pixel for pixel, because the gradients are picked differently. If required, an
exact match can be made by redesigning the gradient array and the permutation table.
To make the difference clear between classic noise and simplex noise, I first present code for
classic Perlin noise in 3D, in an implementation and a coding style that is aimed at being similar
to the simplex noise implementation to facilitate direct comparison. I then move on to 2D sim-
plex noise, which Perlin does not present in his article, continue with my re-implementation of
3D simplex noise, and conclude with 4D simplex noise, which Perlin does not present either.
My code is unnecessarily long-winded, but readable. I tried to stay away from obfuscating
shortcuts. I even avoided loops in order to show exactly what is being done, so there is quite a
lot of stupid cut-and-paste and repetitive code that could be rewritten in a much more compact
form by using short loops and a few small arrays instead of individual named variables.
Like Ken Perlin, I chose Java as the programming language for these examples. The Java code
is very basic and should be easy to port to other languages even if you don’t know Java. In fact,
the code I present is a backport to Java from a GLSL project where I implemented simplex noise
in a fragment shader. Some parts of the code are leftovers from the limitations of GLSL, and
might not be particularly efficient for a pure software implementation. It is faster than Ken Per-
lins reference implementation, but his code is not particularly fast either, because it uses a long
sequence of bit-wise operations on computing a hash value which I instead read directly from a
small permutaton array. Perlin’s code, although it is written in Java, is really aimed at a hard-
ware implementation. So, if you are looking for a fast implementation of simplex noise, the code
on the following pages might not be what you want. It is not dead slow, but it is first and fore-
most explanatory and meant to be read by humans, so it could be speeded up.
One disadvantage of my version is that it uses more memory than Perlin’s simplex noise. I use
a few look-up tables for some stuff that could be implemented in other ways if memory is a tight
or nonexistent resource, e.g. if you are aiming at a slimmed bare-bones hardware implementa-
tion with logic gates and registers, or if you need to do this entirely in a small set of registers in
a DSP architecture or something else with a high penalty for memory accesses. On modern pro-
grammable graphics hardware, there is an abundance of texture memory and texture access
functions with good performance, so look-up tables of reasonable size can be implemented ef-
ficiently, and in software on a general CPU, a few hundred bytes of storage is not a problem.
public class ClassicNoise { // Classic Perlin noise in 3D, for comparison
// To remove the need for index wrapping, double the permutation table length
private static int perm[] = new int[512];
static { for(int i=0; i<512; i++) perm[i]=p[i & 255]; }
// Wrap the integer cells at 255 (smaller integer period can be introduced here)
X = X & 255;
Y = Y & 255;
Z = Z & 255;
// Calculate a set of eight hashed gradient indices
int gi000 = perm[X+perm[Y+perm[Z]]] % 12;
int gi001 = perm[X+perm[Y+perm[Z+1]]] % 12;
int gi010 = perm[X+perm[Y+1+perm[Z]]] % 12;
int gi011 = perm[X+perm[Y+1+perm[Z+1]]] % 12;
int gi100 = perm[X+1+perm[Y+perm[Z]]] % 12;
int gi101 = perm[X+1+perm[Y+perm[Z+1]]] % 12;
int gi110 = perm[X+1+perm[Y+1+perm[Z]]] % 12;
int gi111 = perm[X+1+perm[Y+1+perm[Z+1]]] % 12;
return nxyz;
}
}
public class SimplexNoise { // Simplex noise in 2D, 3D and 4D
// To remove the need for index wrapping, double the permutation table length
private static int perm[] = new int[512];
static { for(int i=0; i<512; i++) perm[i]=p[i & 255]; }
double n0, n1, n2; // Noise contributions from the three corners
// Work out the hashed gradient indices of the three simplex corners
int ii = i & 255;
int jj = j & 255;
int gi0 = perm[ii+perm[jj]] % 12;
int gi1 = perm[ii+i1+perm[jj+j1]] % 12;
int gi2 = perm[ii+1+perm[jj+1]] % 12;
// Add contributions from each corner to get the final noise value.
// The result is scaled to return values in the interval [-1,1].
return 70.0 * (n0 + n1 + n2);
}
// 3D simplex noise
public static double noise(double xin, double yin, double zin) {
double n0, n1, n2, n3; // Noise contributions from the four corners
final double G3 = 1.0/6.0; // Very nice and simple unskew factor, too
double t = (i+j+k)*G3;
double X0 = i-t; // Unskew the cell origin back to (x,y,z) space
double Y0 = j-t;
double Z0 = k-t;
double x0 = xin-X0; // The x,y,z distances from the cell origin
double y0 = yin-Y0;
double z0 = zin-Z0;
if(x0>=y0) {
if(y0>=z0)
{ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
}
else { // x0<y0
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
}
// Work out the hashed gradient indices of the four simplex corners
int ii = i & 255;
int jj = j & 255;
int kk = k & 255;
int gi0 = perm[ii+perm[jj+perm[kk]]] % 12;
int gi1 = perm[ii+i1+perm[jj+j1+perm[kk+k1]]] % 12;
int gi2 = perm[ii+i2+perm[jj+j2+perm[kk+k2]]] % 12;
int gi3 = perm[ii+1+perm[jj+1+perm[kk+1]]] % 12;
// Add contributions from each corner to get the final noise value.
// The result is scaled to stay just inside [-1,1]
return 32.0*(n0 + n1 + n2 + n3);
}
// 4D simplex noise
double noise(double x, double y, double z, double w) {
// The skewing and unskewing factors are hairy again for the 4D case
final double F4 = (Math.sqrt(5.0)-1.0)/4.0;
final double G4 = (5.0-Math.sqrt(5.0))/20.0;
double n0, n1, n2, n3, n4; // Noise contributions from the five corners
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
// To find out which of the 24 possible simplices we're in, we need to
// determine the magnitude ordering of x0, y0, z0 and w0.
// The method below is a good way of finding the ordering of x,y,z,w and
// then find the correct traversal order for the simplex we’re in.
// First, six pair-wise comparisons are performed between each possible pair
// of the four coordinates, and the results are used to add up binary bits
// for an integer index.
int c1 = (x0 > y0) ? 32 : 0;
int c2 = (x0 > z0) ? 16 : 0;
int c3 = (y0 > z0) ? 8 : 0;
int c4 = (x0 > w0) ? 4 : 0;
int c5 = (y0 > w0) ? 2 : 0;
int c6 = (z0 > w0) ? 1 : 0;
int c = c1 + c2 + c3 + c4 + c5 + c6;
int i1, j1, k1, l1; // The integer offsets for the second simplex corner
int i2, j2, k2, l2; // The integer offsets for the third simplex corner
int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
// Work out the hashed gradient indices of the five simplex corners
int ii = i & 255;
int jj = j & 255;
int kk = k & 255;
int ll = l & 255;
int gi0 = perm[ii+perm[jj+perm[kk+perm[ll]]]] % 32;
int gi1 = perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]] % 32;
int gi2 = perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]] % 32;
int gi3 = perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]] % 32;
int gi4 = perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]] % 32;
2D
3D
4D