-
-
Save yaiqsa/cfd7dc56014e2574af8713fa10688015 to your computer and use it in GitHub Desktop.
A slightly modified implementation of Ken Perlin's improved noise that allows for tiling the noise arbitrarily.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class Perlin | |
| { | |
| /// <summary> | |
| /// To my knowledge not used variable in my application. | |
| /// </summary> | |
| public int repeat; | |
| /// <summary> | |
| /// Instance of <see cref="Random"/> class. | |
| /// </summary> | |
| private static Random rand = new Random(); | |
| /// <summary> | |
| /// First overload of the constructor. | |
| /// To my knowledge not used in my application. | |
| /// </summary> | |
| /// <param name="repeat">To my knowledge not used variable in my application.</param> | |
| public Perlin(int repeat = -1) | |
| { | |
| this.repeat = repeat; | |
| } | |
| /// <summary> | |
| /// To my knowledge essential variable. | |
| /// </summary> | |
| private static readonly int[] p = new int[512]; | |
| /// <summary> | |
| /// This method randomizes the Perlin Noise. | |
| /// </summary> | |
| /// <remarks> | |
| /// This randomization is achieved by creating a random version of <see cref="permutation"/>. | |
| /// </remarks> | |
| private static void randomize() | |
| { | |
| int[] table = new int[256]; //Create an empty array | |
| List<int> random = Enumerable.Range(0, 256).ToList(); //Create a list filled with numbers from 0 to 255 | |
| for (int i = 255; i >= 0; i--) | |
| { | |
| int randomNumber = rand.Next(0, i + 1); | |
| table[i] = random[randomNumber]; //Put a number from a random index of the list into the array | |
| random.RemoveAt(randomNumber); //Remove that number from the list | |
| } | |
| permutation = table; | |
| for (int x = 0; x < 512; x++) | |
| { | |
| p[x] = permutation[x % 256]; | |
| } | |
| } | |
| /// <summary> | |
| /// Method to access <see cref="randomize"/> from outside. | |
| /// </summary> | |
| public void Randomize() | |
| { | |
| randomize(); | |
| } | |
| /// <summary> | |
| /// This method returns the actual Perlin Noise value. | |
| /// </summary> | |
| /// <param name="x">x position in the Perlin Noise field. Best result when incremented with 0.0001 to 0.1.</param> | |
| /// <param name="y">y position in the Perlin Noise field. Best result when incremented with 0.0001 to 0.1.</param> | |
| /// <param name="z">z position in the Perlin Noise field. Best result when incremented with 0.0001 to 0.1.</param> | |
| /// <param name="octaves">The amount of refining steps the Perlin Noise value is put trough.</param> | |
| /// <param name="persistence">The amount the amplitude is multiplicated by in each octave.</param> | |
| /// <returns>a <see cref="double"/> Perlin Noise value, between 0 and 1.</returns> | |
| 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; | |
| } | |
| /// <summary> | |
| /// Hash lookup table as defined by Ken Perlin. | |
| /// </summary> | |
| /// <remarks> | |
| /// <para> | |
| /// This is a randomly arranged array of all numbers from 0-255 inclusive. | |
| /// When the <see cref="randomize"/> method is called, this <see cref="Array"/> is overwritten. | |
| /// </para> | |
| /// <para> | |
| /// This used to be read-only in the original GitHub project. | |
| /// </para> | |
| /// <para> | |
| /// Original array: | |
| /// <code> | |
| /// { 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 | |
| /// } | |
| /// </code> | |
| /// </para> | |
| /// </remarks> | |
| private static int[] 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 | |
| }; | |
| /// <summary> | |
| /// Second overload of the constructor. | |
| /// </summary> | |
| /// <remarks> | |
| /// As far as I understand this overload of the constructor is the only one used in my application. | |
| /// </remarks> | |
| static Perlin() | |
| { | |
| randomize(); //This line can be removed to keep the original hash table | |
| //p = new int[512]; //These lines should be uncommented to keep the original hash table. | |
| //for (int x = 0; x < 512; x++) | |
| //{ | |
| // p[x] = permutation[x % 256]; | |
| //} | |
| } | |
| /// <summary> | |
| /// The method that creates a single octave Perlin Noise value. | |
| /// </summary> | |
| /// <remarks> | |
| /// This method used to be public in the original GitHub project, but because I see no use in directly accessing this method (in contrary to <see cref="OctavePerlin(double, double, double, int, double)"/>, which calculates the octaves for you). | |
| /// </remarks> | |
| /// <param name="x">x position in the Perlin Noise field. Best result when incremented with 0.0001 to 0.1.</param> | |
| /// <param name="y">y position in the Perlin Noise field. Best result when incremented with 0.0001 to 0.1.</param> | |
| /// <param name="z">z position in the Perlin Noise field. Best result when incremented with 0.0001 to 0.1.</param> | |
| /// <returns>a single octave <see cref="double"/> Perlin Noise value, between 0 and 1.</returns> | |
| private 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; | |
| 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) | |
| } | |
| /// <summary> | |
| /// To my knowledge not in my application used method. | |
| /// </summary> | |
| /// <param name="num">To my knowledge not in my application used variable.</param> | |
| /// <returns>Product of calculation.</returns> | |
| public int inc(int num) | |
| { | |
| num++; | |
| if (repeat > 0) num %= repeat; | |
| return num; | |
| } | |
| /// <summary> | |
| /// To my knowledge essential method to calculate Perlin Noise values. | |
| /// </summary> | |
| /// <remarks> | |
| /// This method used to be public in the original GitHub project, but because I see no use in directly accessing this method. | |
| /// </remarks> | |
| /// <param name="hash"></param> | |
| /// <param name="x"></param> | |
| /// <param name="y"></param> | |
| /// <param name="z"></param> | |
| /// <returns></returns> | |
| private 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. | |
| } | |
| /// <summary> | |
| /// Fade function as defined by Ken Perlin. | |
| /// </summary> | |
| /// <remarks> | |
| /// <para> | |
| /// This method used to be public in the original GitHub project, but because I see no use in directly accessing this method. | |
| /// </para> | |
| /// <para> | |
| /// This function eases coordinate values so that they will "ease" towards integral values. | |
| /// This ends up smoothing the final output. | |
| /// </para> | |
| /// </remarks> | |
| /// <param name="t">to-be-faded variable.</param> | |
| /// <returns>6t^5 - 15t^4 + 10t^3</returns> | |
| private static double fade(double t) | |
| { | |
| return t * t * t * (t * (t * 6 - 15) + 10); // 6t^5 - 15t^4 + 10t^3 | |
| } | |
| /// <summary> | |
| /// To my knowledge essential method to calculate Perlin Noise values. | |
| /// </summary> | |
| /// <remarks> | |
| /// This method used to be public in the original GitHub project, but because I see no use in directly accessing this method. | |
| /// </remarks> | |
| /// <param name="a">parameter.</param> | |
| /// <param name="b">parameter.</param> | |
| /// <param name="x">parameter.</param> | |
| /// <returns>a + x * (b - a)</returns> | |
| private static double lerp(double a, double b, double x) | |
| { | |
| return a + x * (b - a); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment