Skip to content

Instantly share code, notes, and snippets.

@Flafla2
Last active May 10, 2023 00:55
Embed
What would you like to do?
A slightly modified implementation of Ken Perlin's improved noise that allows for tiling the noise arbitrarily.
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);
}
}
@PAEz
Copy link

PAEz commented Aug 10, 2014

@eAi
Copy link

eAi commented Apr 1, 2016

Line 61 has a typo - the extra 'i' at the end.

Copy link

ghost commented May 1, 2017

Good catch @eAi

@LiekeVanmulken
Copy link

LiekeVanmulken commented Oct 5, 2017

I liked this a lot, so i made a js implementation of it, which can be found here .

@Solido
Copy link

Solido commented Aug 8, 2019

@DerDu
Copy link

DerDu commented Dec 28, 2020

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);
    }
}

@maybebool
Copy link

Great explanation. Thanks for the work.

@Leslieghf
Copy link

Leslieghf commented Sep 24, 2022

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

@eyeofparadox
Copy link

Anyone know of a Lua version?

@DavidAntliff
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment