-
-
Save Flafla2/f0260a861be0ebdeef76 to your computer and use it in GitHub Desktop.
public class Perlin { | |
public int repeat; | |
public Perlin(int repeat = -1) { | |
this.repeat = repeat; | |
} | |
public double OctavePerlin(double x, double y, double z, int octaves, double persistence) { | |
double total = 0; | |
double frequency = 1; | |
double amplitude = 1; | |
double maxValue = 0; // Used for normalizing result to 0.0 - 1.0 | |
for(int i=0;i<octaves;i++) { | |
total += perlin(x * frequency, y * frequency, z * frequency) * amplitude; | |
maxValue += amplitude; | |
amplitude *= persistence; | |
frequency *= 2; | |
} | |
return total/maxValue; | |
} | |
private static readonly int[] permutation = { 151,160,137,91,90,15, // Hash lookup table as defined by Ken Perlin. This is a randomly | |
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, // arranged array of all numbers from 0-255 inclusive. | |
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, | |
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, | |
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, | |
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, | |
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, | |
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, | |
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, | |
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, | |
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, | |
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, | |
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 | |
}; | |
private static readonly int[] p; // Doubled permutation to avoid overflow | |
static Perlin() { | |
p = new int[512]; | |
for(int x=0;x<512;x++) { | |
p[x] = permutation[x%256]; | |
} | |
} | |
public double perlin(double x, double y, double z) { | |
if(repeat > 0) { // If we have any repeat on, change the coordinates to their "local" repetitions | |
x = x%repeat; | |
y = y%repeat; | |
z = z%repeat; | |
} | |
int xi = (int)x & 255; // Calculate the "unit cube" that the point asked will be located in | |
int yi = (int)y & 255; // The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that | |
int zi = (int)z & 255; // plus 1. Next we calculate the location (from 0.0 to 1.0) in that cube. | |
double xf = x-(int)x; // We also fade the location to smooth the result. | |
double yf = y-(int)y;i | |
double zf = z-(int)z; | |
double u = fade(xf); | |
double v = fade(yf); | |
double w = fade(zf); | |
int aaa, aba, aab, abb, baa, bba, bab, bbb; | |
aaa = p[p[p[ xi ]+ yi ]+ zi ]; | |
aba = p[p[p[ xi ]+inc(yi)]+ zi ]; | |
aab = p[p[p[ xi ]+ yi ]+inc(zi)]; | |
abb = p[p[p[ xi ]+inc(yi)]+inc(zi)]; | |
baa = p[p[p[inc(xi)]+ yi ]+ zi ]; | |
bba = p[p[p[inc(xi)]+inc(yi)]+ zi ]; | |
bab = p[p[p[inc(xi)]+ yi ]+inc(zi)]; | |
bbb = p[p[p[inc(xi)]+inc(yi)]+inc(zi)]; | |
double x1, x2, y1, y2; | |
x1 = lerp( grad (aaa, xf , yf , zf), // The gradient function calculates the dot product between a pseudorandom | |
grad (baa, xf-1, yf , zf), // gradient vector and the vector from the input coordinate to the 8 | |
u); // surrounding points in its unit cube. | |
x2 = lerp( grad (aba, xf , yf-1, zf), // This is all then lerped together as a sort of weighted average based on the faded (u,v,w) | |
grad (bba, xf-1, yf-1, zf), // values we made earlier. | |
u); | |
y1 = lerp(x1, x2, v); | |
x1 = lerp( grad (aab, xf , yf , zf-1), | |
grad (bab, xf-1, yf , zf-1), | |
u); | |
x2 = lerp( grad (abb, xf , yf-1, zf-1), | |
grad (bbb, xf-1, yf-1, zf-1), | |
u); | |
y2 = lerp (x1, x2, v); | |
return (lerp (y1, y2, w)+1)/2; // For convenience we bound it to 0 - 1 (theoretical min/max before is -1 - 1) | |
} | |
public int inc(int num) { | |
num++; | |
if (repeat > 0) num %= repeat; | |
return num; | |
} | |
public static double grad(int hash, double x, double y, double z) { | |
int h = hash & 15; // Take the hashed value and take the first 4 bits of it (15 == 0b1111) | |
double u = h < 8 /* 0b1000 */ ? x : y; // If the most significant bit (MSB) of the hash is 0 then set u = x. Otherwise y. | |
double v; // In Ken Perlin's original implementation this was another conditional operator (?:). I | |
// expanded it for readability. | |
if(h < 4 /* 0b0100 */) // If the first and second significant bits are 0 set v = y | |
v = y; | |
else if(h == 12 /* 0b1100 */ || h == 14 /* 0b1110*/)// If the first and second significant bits are 1 set v = x | |
v = x; | |
else // If the first and second significant bits are not equal (0/1, 1/0) set v = z | |
v = z; | |
return ((h&1) == 0 ? u : -u)+((h&2) == 0 ? v : -v); // Use the last 2 bits to decide if u and v are positive or negative. Then return their addition. | |
} | |
public static double fade(double t) { | |
// Fade function as defined by Ken Perlin. This eases coordinate values | |
// so that they will "ease" towards integral values. This ends up smoothing | |
// the final output. | |
return t * t * t * (t * (t * 6 - 15) + 10); // 6t^5 - 15t^4 + 10t^3 | |
} | |
public static double lerp(double a, double b, double x) { | |
return a + x * (b - a); | |
} | |
} |
PHP Version
<?php
declare(strict_types=1);
namespace App\Noise;
/**
* Class Generator
*
* @package App\Noise
*/
class Generator
{
/**
* @var int
*/
private int $repeat;
/**
* @var int[]
*/
// @formatter:off
private array $permutation = [151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180];
// @formatter:on
/**
* @var int[]
*/
private array $p = [];
public function __construct(int $repeat= -1)
{
$this->repeat= $repeat;
for ($x = 0; $x < 512; $x++) {
$this->p[$x] = $this->permutation[$x % 256];
}
}
public function noise(float $x, float $y, float $z, int $octaves, float $persistence): float
{
$total = 0.0;
$frequency = 1.0;
$amplitude = 1.0;
$maxValue = 0.0;
for ($i = 0; $i < $octaves; $i++) {
$total += $this->perlin($x * $frequency, $y * $frequency, $z * $frequency) * $amplitude;
$maxValue += $amplitude;
$amplitude *= $persistence;
$frequency *= 2;
}
return $total / $maxValue;
}
private function perlin(float $x, float $y, float $z): float
{
if ($this->seed > 0) {
$x = $x % $this->repeat;
$y = $y % $this->repeat;
$z = $z % $this->repeat;
}
$xi = (int)$x & 255;
$yi = (int)$y & 255;
$zi = (int)$z & 255;
$xf = $x - (int)$x;
$yf = $y - (int)$y;
$zf = $z - (int)$z;
$u = $this->fade($xf);
$v = $this->fade($yf);
$w = $this->fade($zf);
$aaa = $this->p[$this->p[$this->p[$xi] + $yi] + $zi];
$aba = $this->p[$this->p[$this->p[$xi] + $this->inc($yi)] + $zi];
$aab = $this->p[$this->p[$this->p[$xi] + $yi] + $this->inc($zi)];
$abb = $this->p[$this->p[$this->p[$xi] + $this->inc($yi)] + $this->inc($zi)];
$baa = $this->p[$this->p[$this->p[$this->inc($xi)] + $yi] + $zi];
$bba = $this->p[$this->p[$this->p[$this->inc($xi)] + $this->inc($yi)] + $zi];
$bab = $this->p[$this->p[$this->p[$this->inc($xi)] + $yi] + $this->inc($zi)];
$bbb = $this->p[$this->p[$this->p[$this->inc($xi)] + $this->inc($yi)] + $this->inc($zi)];
$x1 = $this->lerp($this->grad($aaa, $xf, $yf, $zf), $this->grad($baa, $xf - 1, $yf, $zf), $u);
$x2 = $this->lerp($this->grad($aba, $xf, $yf - 1, $zf), $this->grad($bba, $xf - 1, $yf - 1, $zf), $u);
$y1 = $this->lerp($x1, $x2, $v);
$x1 = $this->lerp($this->grad($aab, $xf, $yf, $zf - 1), $this->grad($bab, $xf - 1, $yf, $zf - 1), $u);
$x2 = $this->lerp($this->grad($abb, $xf, $yf - 1, $zf - 1), $this->grad($bbb, $xf - 1, $yf - 1, $zf - 1), $u);
$y2 = $this->lerp($x1, $x2, $v);
return ($this->lerp($y1, $y2, $w) + 1) / 2;
}
private function fade(float $t): float
{
return $t * $t * $t * ($t * ($t * 6 - 15) + 10);// 6t^5 - 15t^4 + 10t^3
}
private function inc(int $num): int
{
$num++;
if ($this->repeat > 0) {
$num %= $this->repeat;
}
return $num;
}
private function lerp(float $a, float $b, float $x): float
{
return $a + $x * ($b - $a);
}
private function grad(int $hash, float $x, float $y, float $z): float
{
$h = $hash & 15;
$u = ($h < 8 ? $x : $y);
if ($h < 4) {
$v = $y;
} elseif ($h == 12 || $h == 14) {
$v = $x;
} else {
$v = $z;
}
return (($h & 1) == 0 ? $u : -$u) + (($h & 2) == 0 ? $v : -$v);
}
}
Great explanation. Thanks for the work.
HLSL / Unity Compute Shader Version: https://gist.github.com/Leslieghf/334b8cd6ed75fb35ae334416627995ee
Disclaimer: I slightly modified this, namingly removing tileability and adding octave offsets (which have to be given by the CPU, as I couldn't manage to create a Seeded Ranged PRNG on the GPU).
If you have questions regarding How to use this or How to modify this, feel free to contact me :D
Anyone know of a Lua version?
Thanks for this, and the associated article, Adrian.
Could someone explain the meaning of the repeat
value please? I don't understand what it's actually doing. The only sensible result I've managed to get is 4x4 "tiles" over 512x512 pixels when repeat
is 4, and x
and y
are from 0.0 to 1.0 over their ranges. But if I try to apply this to other values of repeat
(e.g. 1, 3, or 64, or 7) I just get weird diagonal striping, or vertical lines, or generally unpleasant output.
Anyone know of a Lua version?
Here ya go. Just so you know, Lua supports bitwise operators now, so depending on which version you're using you may have to go in and switch out bit32.band()
https://gist.github.com/kymckay/25758d37f8e3872e1636d90ad41fe2ed
What is the license of this code?
This does some weird stuff when y is negative.... the output goes way outside 0 and 1.
Should double xf = x-(int)x;
be.... double xf = Math.abs(x-(int)x)
? Or somehow reverse into another unit square?
Fix - the xi and xf variables need to be using 'floor' instead of int casting, which will take -2.4 to -2 instead of -3 (there may be a faster way, please let me know if you find it 😄 )
let xi = Math.floor(x) & 255;
let yi = Math.floor(y) & 255;
let zi = Math.floor(z) & 255;
let xf = x - Math.floor(x);
let yf = y - Math.floor(y);
let zf = z - Math.floor(z);
Fixed the bug in the code.
public class Perlin {
public int repeat;
public Perlin(int repeat = -1) {
this.repeat = repeat;
}
public double OctavePerlin(double x, double y, double z, int octaves, double persistence) {
double total = 0;
double frequency = 1;
double amplitude = 1;
double maxValue = 0; // Used for normalizing result to 0.0 - 1.0
for(int i=0;i<octaves;i++) {
total += perlin(x * frequency, y * frequency, z * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= 2;
}
return total/maxValue;
}
private static readonly int[] permutation = { 151,160,137,91,90,15, // Hash lookup table as defined by Ken Perlin. This is a randomly
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, // arranged array of all numbers from 0-255 inclusive.
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
private static readonly int[] p; // Doubled permutation to avoid overflow
static Perlin() {
p = new int[512];
for(int x=0;x<512;x++) {
p[x] = permutation[x%256];
}
}
public double perlin(double x, double y, double z) {
if(repeat > 0) { // If we have any repeat on, change the coordinates to their "local" repetitions
x = x%repeat;
y = y%repeat;
z = z%repeat;
}
int xi = (int)Math.Floor(x) & 255; // Calculate the "unit cube" that the point asked will be located in
int yi = (int)Math.Floor(y) & 255; // The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that
int zi = (int)Math.Floor(z) & 255; // plus 1. Next we calculate the location (from 0.0 to 1.0) in that cube.
double xf = x - Math.Floor(x); // We also fade the location to smooth the result.
double yf = y - Math.Floor(y);
double zf = z - Math.Floor(z);
double u = fade(xf);
double v = fade(yf);
double w = fade(zf);
int aaa, aba, aab, abb, baa, bba, bab, bbb;
aaa = p[p[p[ xi ]+ yi ]+ zi ];
aba = p[p[p[ xi ]+inc(yi)]+ zi ];
aab = p[p[p[ xi ]+ yi ]+inc(zi)];
abb = p[p[p[ xi ]+inc(yi)]+inc(zi)];
baa = p[p[p[inc(xi)]+ yi ]+ zi ];
bba = p[p[p[inc(xi)]+inc(yi)]+ zi ];
bab = p[p[p[inc(xi)]+ yi ]+inc(zi)];
bbb = p[p[p[inc(xi)]+inc(yi)]+inc(zi)];
double x1, x2, y1, y2;
x1 = lerp( grad (aaa, xf , yf , zf), // The gradient function calculates the dot product between a pseudorandom
grad (baa, xf-1, yf , zf), // gradient vector and the vector from the input coordinate to the 8
u); // surrounding points in its unit cube.
x2 = lerp( grad (aba, xf , yf-1, zf), // This is all then lerped together as a sort of weighted average based on the faded (u,v,w)
grad (bba, xf-1, yf-1, zf), // values we made earlier.
u);
y1 = lerp(x1, x2, v);
x1 = lerp( grad (aab, xf , yf , zf-1),
grad (bab, xf-1, yf , zf-1),
u);
x2 = lerp( grad (abb, xf , yf-1, zf-1),
grad (bbb, xf-1, yf-1, zf-1),
u);
y2 = lerp (x1, x2, v);
return (lerp (y1, y2, w)+1)/2; // For convenience we bound it to 0 - 1 (theoretical min/max before is -1 - 1)
}
public int inc(int num) {
num++;
if (repeat > 0) num %= repeat;
return num;
}
public static double grad(int hash, double x, double y, double z) {
int h = hash & 15; // Take the hashed value and take the first 4 bits of it (15 == 0b1111)
double u = h < 8 /* 0b1000 */ ? x : y; // If the most significant bit (MSB) of the hash is 0 then set u = x. Otherwise y.
double v; // In Ken Perlin's original implementation this was another conditional operator (?:). I
// expanded it for readability.
if(h < 4 /* 0b0100 */) // If the first and second significant bits are 0 set v = y
v = y;
else if(h == 12 /* 0b1100 */ || h == 14 /* 0b1110*/)// If the first and second significant bits are 1 set v = x
v = x;
else // If the first and second significant bits are not equal (0/1, 1/0) set v = z
v = z;
return ((h&1) == 0 ? u : -u)+((h&2) == 0 ? v : -v); // Use the last 2 bits to decide if u and v are positive or negative. Then return their addition.
}
public static double fade(double t) {
// Fade function as defined by Ken Perlin. This eases coordinate values
// so that they will "ease" towards integral values. This ends up smoothing
// the final output.
return t * t * t * (t * (t * 6 - 15) + 10); // 6t^5 - 15t^4 + 10t^3
}
public static double lerp(double a, double b, double x) {
return a + x * (b - a);
}
}
I liked this a lot, so i made a js implementation of it, which can be found here .