Skip to content

Commit 917f809

Browse files
committed
Initial commit
1 parent bb272a1 commit 917f809

File tree

4 files changed

+337
-1
lines changed

4 files changed

+337
-1
lines changed

README.md

+109-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,109 @@
1-
# SimplexNoise
1+
# SimplexNoise
2+
3+
## Usage
4+
5+
```javascript
6+
// simplex_noise_2d.js
7+
seed(1337);
8+
let value = fbm(x, y, octaves, amplitude, frequency, persistence, lacunarity);
9+
10+
// simplex_noise_2d.min.js
11+
s(1337);
12+
let value = b(x, y, octaves, amplitude, frequency, persistence, lacunarity);
13+
```
14+
15+
## Simplex noise
16+
17+
[Simplex noise](https://en.wikipedia.org/wiki/Simplex_noise) is the result of an $n$-dimensional noise function comparable to [Perlin noise](https://en.wikipedia.org/wiki/Perlin_noise) ("classic" noise) but with fewer directional artifacts and, in higher dimensions, a lower computational overhead. [Ken Perlin](https://en.wikipedia.org/wiki/Ken_Perlin) designed the algorithm in 2001 to address the limitations of his classic noise function, especially in higher dimensions.
18+
19+
The advantages of simplex noise over Perlin noise:
20+
21+
- Simplex noise has lower computational complexity and requires fewer multiplications.
22+
- Simplex noise scales to higher dimensions (4D, 5D) with much less computational cost: the complexity is $O(n^2)$ for $n$ dimensions instead of the $O(n2^n)$ of classic noise.
23+
- Simplex noise has no noticeable directional artifacts (is visually [isotropic](https://en.wikipedia.org/wiki/Isotropy)), though noise generated for different dimensions is visually distinct (e.g. 2D noise has a different look than 2D slices of 3D noise, and it looks increasingly worse for higher dimensions).
24+
- Simplex noise has a well-defined and continuous gradient (almost) everywhere that can be computed quite cheaply.
25+
- Simplex noise is easy to implement in hardware.
26+
27+
Whereas classical noise interpolates between the [gradients](https://en.wikipedia.org/wiki/Gradient) at the surrounding hypergrid end points (i.e., northeast, northwest, southeast and southwest in 2D), simplex noise divides the space into [simplices](https://en.wikipedia.org/wiki/Simplex) (i.e., $n$-dimensional triangles). This reduces the number of data points. While a hypercube in $n$ dimensions has $2^n$ corners, a simplex in $n$ dimensions has only $n+1$ corners. The triangles are [equilateral](https://en.wikipedia.org/wiki/Equilateral_triangle) in 2D, but in higher dimensions the simplices are only approximately regular. For example, the tiling in the 3D case of the function is an orientation of the [tetragonal disphenoid honeycomb](https://en.wikipedia.org/wiki/Tetragonal_disphenoid_honeycomb).
28+
29+
Simplex noise is useful for computer graphics applications, where noise is usually computed over 2, 3, 4, or possibly 5 dimensions. For higher dimensions, n-spheres around n-simplex corners are not densely enough packed, reducing the support of the function and making it zero in large portions of space.
30+
31+
## About Perlin's "Simplex" Noise
32+
33+
- Perlin's "Classic" Noise (1984) is an algorithm producing pseudo-random fluctuations simulating natural looking variations, producing paterns all of the same size. It is a kind of gradiant-noise algorithm, invented by Ken Perlin while working on visual special effects for the Tron movie (1982). It works by interpolating pseudo-random gradiants defined in a multi-dimensionnal grid. [Ken Perlin original references](http://mrl.nyu.edu/~perlin/doc/oscar.html)
34+
- Perlin's "Improved" Noise (2002) switches to a new interpolation fonction with a 2nd derivative zero at t=0 and t=1 to remove artifacts on integer values, and switches to using predefined gradients of unit lenght to the middle of each edges. [Ken Perlin original references](http://mrl.nyu.edu/~perlin/paper445.pdf)
35+
- Perlin's "Simplex" Noise (2001) rather than placing each input point into a cubic grid, based on the integer parts of its (x,y,z) coordinate values, placed them onto a simplicial grid (think triangles instead of squares, pyramids instead of cubes...) [Ken Perlin original references](http://www.csee.umbc.edu/~olano/s2002c36/ch02.pdf)
36+
37+
## Fractional Brownian Motion
38+
39+
[Fractional Brownian Motion](https://en.wikipedia.org/wiki/Fractional_Brownian_motion) is the summation of successive octaves of noise, each with higher frequency and lower amplitude. It doesn't especially matter what kind of noise, most will do. Fractional Brownian Motion (often abbreviated as fBm) is often confused with noise algorithms like [Value Noise](https://en.wikipedia.org/wiki/Value_noise) and Perlin Noise, when in fact it just takes a type of noise and makes a more interesting picture.
40+
41+
```javascript
42+
function fBm(x, y, octaves, amplitude, frequency, persistence, lacunarity) {
43+
let total = 0;
44+
45+
for (let i = 0; i < octaves; i++) {
46+
total += noise(x * frequency, y * frequency) * amplitude;
47+
frequency *= lacunarity;
48+
amplitude *= persistence;
49+
}
50+
51+
return total;
52+
}
53+
```
54+
55+
There are five terms here: octaves, amplitude, frequency, lacunarity, and persistence.
56+
57+
**Octaves** are how many layers you are putting together. If you start with big features, the number of octaves determines how detailed the map will look.
58+
59+
The **amplitude** is how tall the features should be. Frequency determines the width of features, amplitude determines the height. Each octave the amplitude shrinks, meaning small features are also short. This doesn't have to be the case, but for this case it makes pleasing maps.
60+
61+
The **frequency** of a layer is how many points fit into the space you've created. So for the mountain scale, you only need a few points, but at the rock scale you may need hundreds of points. In the code above, I start with a small frequency (which equates to large features) and move to higher frequencies which produce smaller details.
62+
63+
**Persistence**, also called **gain**, is what makes the amplitude shrink (or not shrink). Each octave the amplitude is multiplied by the gain. I use a gain of 0.65. If it is higher then the amplitude will barely shrink, and maps get crazy. Too low and the details become miniscule, and the map looks washed out. However, most use 1/lacunarity. Since the standard for lacunarity is 2.0, the standard for the gain is 0.5. Noise that has a gain of 0.5 and a lacunarity of 2.0 is referred to as 1/f noise, and is the industry standard.
64+
65+
**Lacunarity** is what makes the frequency grow. Each octave the frequency is multiplied by the lacunarity. I use a lacunarity of 2.0, however values of 1.8715 or 2.1042 can help to reduce artifacts in some algorithms. A lacunarity of 2.0 means that the frequency doubles each octave, so if the first octave had 3 points the second would have 6, then 12, then 24, etc. This is used almost exclusively, partly because octaves in music double in frequency. Other values are perfectly acceptable, but the results will vary.
66+
67+
## SplitMix32
68+
69+
SplitMix32 is a transformation of the `fmix32` finalizer from MurmurHash3 into a PRNG. It has a 32-bit internal state, like Xorshift and Mulberry32.
70+
71+
```js
72+
function splitmix32(a) {
73+
return function() {
74+
a |= 0; a = a + 0x9e3779b9 | 0;
75+
var t = a ^ a >>> 16; t = Math.imul(t, 0x21f0aaad);
76+
t = t ^ t >>> 15; t = Math.imul(t, 0x735a2d97);
77+
return ((t = t ^ t >>> 15) >>> 0) / 4294967296;
78+
}
79+
}
80+
```
81+
82+
This is based on an algorithm known as `SplitMix` included in Java JDK8. It uses 64-bit arithmetic and doesn't define a 32-bit version. However, It is derived from the `fmix64` finalizer used in MurmurHash3 and appears to be an application of Weyl sequences. MurmurHash3 also contains a 32-bit equivalent of this function, `fmix32`. The constant `0x9e3779b` is the 32-bit truncation of the golden ratio, which is also what is used in the original.
83+
84+
## References
85+
86+
- **Simplex Noise**
87+
- [Wikipedia, Simplex Noise](https://en.wikipedia.org/wiki/Simplex_noise)
88+
- [Wikipedia, Perlin noise](https://en.wikipedia.org/wiki/Perlin_noise)
89+
- [Patent, Standard for perlin noise](https://www.google.com/patents/US6867776)
90+
- [The Perlin noise math FAQ](https://web.archive.org/web/20120107051039/http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html)
91+
- [Perlin Noise](https://web.archive.org/web/20160530124230/http://freespace.virgin.net/hugo.elias/models/m_perlin.htm)
92+
- [Ken Perlin's "Noise Hardware" Reference Implementation](http://www.csee.umbc.edu/~olano/s2002c36/ch02.pdf)
93+
- [Improving Noise, Ken Perlin](https://mrl.cs.nyu.edu/~perlin/paper445.pdf)
94+
- [Improved Noise reference implementation](https://mrl.cs.nyu.edu/~perlin/noise/)
95+
- [Stefan Gustavson's Simplex noise demystified](https://web.archive.org/web/20160315144303/http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf)
96+
- [Improved Simplex noise demystified Java Implementation](https://web.archive.org/web/20180711022502/http://webstaff.itn.liu.se/~stegu/simplexnoise/SimplexNoise.java)
97+
- [The Book of Shaders, Noise](https://thebookofshaders.com/11/)
98+
- [SimplexNoise](https://github.com/SRombauts/SimplexNoise)
99+
- **Fractional Brownian Motion**
100+
- [Wikipedia, Fractional Brownian Motion](https://en.wikipedia.org/wiki/Fractional_Brownian_motion)
101+
- [The Book of Shaders, Fractal Brownian Motion](https://thebookofshaders.com/13/)
102+
- [Fractional Brownian Motion, Inigo Quilez](https://iquilezles.org/articles/fbm/)
103+
- [FBM](https://code.google.com/archive/p/fractalterraingeneration/wikis/Fractional_Brownian_Motion.wiki)
104+
- **SplitMix32**
105+
- [Wikipedia, Pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator)
106+
- [Pseudorandom number generators](https://github.com/bryc/code/blob/master/jshash/PRNGs.md#splitmix32)
107+
- [Fast Splittable Pseudorandom Number Generators](https://gee.cs.oswego.edu/dl/papers/oopsla14.pdf)
108+
- [SplitMix32](https://github.com/joelkp/ranoise#other-interesting-functions)
109+
- [splitmix32.c](https://github.com/umireon/my-random-stuff/blob/master/xorshift/splitmix32.c)

demo.html

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<html>
2+
<head>
3+
<style>
4+
body {overflow: hidden; margin: 0; padding: 0}
5+
</style>
6+
</head>
7+
<body>
8+
<canvas></canvas>
9+
<script src="simplex_noise_2d.min.js"></script>
10+
<script>
11+
const canvas = document.querySelector("canvas");
12+
const ctx = canvas.getContext("2d");
13+
14+
const width = canvas.width = window.innerWidth;
15+
const height = canvas.height = window.innerHeight;
16+
const size = width / 256;
17+
const pixels = 2 / 150;
18+
19+
s(1337);
20+
21+
for (let y = 0; y < 2; y += pixels / 2) {
22+
for (let x = 0; x < 2; x += pixels / 2) {
23+
ctx.fillStyle = `hsl(${parseInt(b(x, y, 8, 1, 4, 0.5, 2.0) * 250)}, 50%, 50%)`;
24+
ctx.fillRect(x / 2 * canvas.width, y / 2 * canvas.width, size, size);
25+
}
26+
}
27+
</script>
28+
</body>
29+
</html>

simplex_noise_2d.js

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/**
2+
* Simplex noise
3+
*
4+
* This JavaScript implementation is based on the speed-improved Java version
5+
* 2012-03-09 by Stefan Gustavson (original Java source code in the public domain).
6+
*
7+
* {@link https://web.archive.org/web/20180711022502/http://webstaff.itn.liu.se/~stegu/simplexnoise/SimplexNoise.java}:
8+
* - Based on example code by Stefan Gustavson ([email protected]).
9+
* - Optimisations by Peter Eastman ([email protected]).
10+
* - Better rank ordering method by Stefan Gustavson in 2012.
11+
*/
12+
const tableSize = 512;
13+
14+
var perm = new Uint8Array(tableSize);
15+
var permMod12 = new Uint8Array(tableSize);
16+
17+
// Skewing and unskewing factors for 2 dimensions
18+
const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);
19+
const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;
20+
21+
/**
22+
* Fractal/Fractional Brownian Motion (fBm) summation of 2D Perlin Simplex noise.
23+
*
24+
* References:
25+
* {@link https://en.wikipedia.org/wiki/Fractional_Brownian_motion}
26+
* {@link https://thebookofshaders.com/13/}
27+
* {@link https://iquilezles.org/articles/fbm/}
28+
*
29+
* @param {number} x - x coordinate
30+
* @param {number} y - y coordinate
31+
* @param {number} octaves - Number of layers of Simplex noise to combine
32+
* @param {number} amplitude - Amplitude ("height") of the first octave of noise
33+
* @param {number} frequency - Frequency ("width") of the first octave of noise
34+
* @param {number} persistence - Persistence is the loss of amplitude between successive octaves
35+
* @param {number} lacunarity - Lacunarity specifies the frequency multiplier between successive octaves
36+
* @returns {number} Noise value in the range[-1; 1], value of 0 on all integer coordinates
37+
*/
38+
const fbm = (x, y, octaves, amplitude, frequency, persistence, lacunarity) => {
39+
let total = 0;
40+
41+
for (let i = 0; i < octaves; i++) {
42+
total += noise(x * frequency, y * frequency) * amplitude; // Get the noise sample
43+
amplitude *= persistence;
44+
frequency *= lacunarity;
45+
}
46+
47+
return total;
48+
};
49+
50+
/**
51+
* Helper function to compute gradients-dot-residual vectors (2D).
52+
*
53+
* References:
54+
* {@link https://mrl.cs.nyu.edu/~perlin/noise/}
55+
*
56+
* @param {number} hash - Hash value
57+
* @param {number} x - x coord of the distance to the corner
58+
* @param {number} y - y coord of the distance to the corner
59+
* @returns {number} Gradient value
60+
*/
61+
const grad = (hash, x, y) => {
62+
let h = hash & 0x0F;
63+
let u = h < 8 ? x : y;
64+
let v = h < 4 ? y : 0;
65+
66+
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
67+
};
68+
69+
/**
70+
* 2D Perlin simplex noise.
71+
*
72+
* References:
73+
* {@link https://web.archive.org/web/20180711022502/http://webstaff.itn.liu.se/~stegu/simplexnoise/SimplexNoise.java}
74+
*
75+
* @param {number} x - x coordinate
76+
* @param {number} y - y coordinate
77+
* @returns {number} Noise value in the range[-1; 1], value of 0 on all integer coordinates
78+
*/
79+
const noise = (xin, yin) => {
80+
let n0, n1, n2; // Noise contributions from the three corners
81+
82+
// Skew the input space to determine which simplex cell we're in
83+
let s = (xin + yin) * F2; // Hairy factor for 2D
84+
let i = Math.floor(xin + s);
85+
let j = Math.floor(yin + s);
86+
let t = (i + j) * G2;
87+
88+
// (i - t) Unskew the cell origin back to (x,y) space
89+
let x0 = xin - (i - t); // The x,y distances from the cell origin
90+
let y0 = yin - (j - t);
91+
92+
// For the 2D case, the simplex shape is an equilateral triangle.
93+
// Determine which simplex we are in.
94+
let i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
95+
if (x0 > y0) {
96+
[i1, j1] = [1, 0]; // lower triangle, XY order: (0,0)->(1,0)->(1,1)
97+
} else {
98+
[i1, j1] = [0, 1]; // upper triangle, YX order: (0,0)->(0,1)->(1,1)
99+
}
100+
101+
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
102+
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
103+
// c = (3-sqrt(3))/6
104+
let x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
105+
let y1 = y0 - j1 + G2;
106+
let x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
107+
let y2 = y0 - 1.0 + 2.0 * G2;
108+
109+
// Work out the hashed gradient indices of the three simplex corners
110+
i &= 255;
111+
j &= 255;
112+
113+
let gi0 = permMod12[i + perm[j]];
114+
let gi1 = permMod12[i + i1 + perm[j + j1]];
115+
let gi2 = permMod12[i + 1 + perm[j + 1]];
116+
117+
// Calculate the contribution from the three corners
118+
let t0 = 0.5 - x0 * x0 - y0 * y0;
119+
if (t0 < 0) {
120+
n0 = 0.0;
121+
} else {
122+
t0 *= t0;
123+
n0 = t0 * t0 * grad(gi0, x0, y0); // (x,y) of grad used for 2D gradient
124+
}
125+
126+
let t1 = 0.5 - x1 * x1 - y1 * y1;
127+
if (t1 < 0) {
128+
n1 = 0.0;
129+
} else {
130+
t1 *= t1;
131+
n1 = t1 * t1 * grad(gi1, x1, y1);
132+
}
133+
134+
let t2 = 0.5 - x2 * x2 - y2 * y2;
135+
if (t2 < 0) {
136+
n2 = 0.0;
137+
} else {
138+
t2 *= t2;
139+
n2 = t2 * t2 * grad(gi2, x2, y2);
140+
}
141+
142+
// Add contributions from each corner to get the final noise value.
143+
// The result is scaled to return values in the interval [-1,1].
144+
return 70.0 * (n0 + n1 + n2);
145+
};
146+
147+
/**
148+
* SplitMix32 is a splittable pseudorandom number generator (PRNG).
149+
*
150+
* Guy L. Steele, Jr., Doug Lea, and Christine H. Flood. 2014. Fast splittable
151+
* pseudorandom number generators. In Proceedings of the 2014 ACM International
152+
* Conference on Object Oriented Programming Systems Languages & Applications
153+
* (OOPSLA '14). ACM, New York, NY, USA, 453-472.
154+
* {@link DOI https://doi.org/10.1145/2660193.2660195}
155+
*
156+
* References:
157+
* {@link https://github.com/bryc/code/blob/master/jshash/PRNGs.md#splitmix32}
158+
* {@link https://github.com/joelkp/ranoise#splitmix32a}
159+
* {@link https://gee.cs.oswego.edu/dl/papers/oopsla14.pdf}
160+
* {@link https://github.com/umireon/my-random-stuff/blob/master/xorshift/splitmix32.c}
161+
*
162+
* @param {number} a - Seed value for SplitMix32
163+
* @returns {function}
164+
*/
165+
const splitmix32 = a => {
166+
return () => {
167+
a |= 0; a = a + 0x9e3779b9 | 0;
168+
var t = a ^ a >>> 16; t = Math.imul(t, 0x21f0aaad);
169+
t = t ^ t >>> 15; t = Math.imul(t, 0x735a2d97);
170+
return ((t = t ^ t >>> 15) >>> 0) / 4294967296;
171+
};
172+
};
173+
174+
/**
175+
* Builds a random permutation table.
176+
*
177+
* @param {number} seed - Seed value for SplitMix32
178+
* @returns {void}
179+
*/
180+
const seed = seed => {
181+
const random = splitmix32(seed);
182+
183+
let p = new Uint8Array(tableSize);
184+
185+
for (let i = 0; i < tableSize / 2; i++) {
186+
p[i] = i;
187+
}
188+
189+
for (let i = 0; i < tableSize / 2 - 1; i++) {
190+
let r = i + ~~(random() * (256 - i));
191+
[p[r], p[i]] = [p[i], p[r]];
192+
}
193+
194+
for (let i = 0; i < tableSize; i++) {
195+
perm[i] = p[i & 255];
196+
permMod12[i] = perm[i] % 12;
197+
}
198+
};

simplex_noise_2d.min.js

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)