Perlin noise, dezvoltat de Ken Perlin în 1983, este o funcție de zgomot gradient utilizată pe scară largă în grafica computerizată pentru a crea texturi, terenuri și alte conținuturi procedurale cu aspect natural. Spre deosebire de zgomotul aleatoriu simplu, Perlin noise produce modele mai netede și mai coerente care imită neregularitățile întâlnite în natură.
Pentru a genera o textură, Perlin noise primește ca input 2 parametri, x și y, coordonatele pixelilor din textură înmulțite cu un număr mic (frecvența).
for y in range(height): for x in range(width): n = perlin(x * frequency, y * frequency)
În implementare, se definește un grid, pe care va fi definită textura. Inpututul algoritmului se va afla într-o celulă a acestui grid. Pentru fiecare colț al fiecărei celule din grid se va genera un vector gradient constant. Outputul algoritmului va fi o interporlare între 4 valori, calculate în funcție de vectorul gradient al fiecărui colț și de datele de input, x și y.
Pentru a calcula valorile necesare interpolării, se vor calcula 4 produse scalare. Pentru fiecare produs scalar, primul vector va fi generat pornind de la colțul curent la punctul de input, iar al doilea vector este vectorul gradient asignat fiecărui colț al gridului.
xf = x - floor(x) yf = y - floor(y) topRight = Vector2(xf - 1.0, yf - 1.0) topLeft = Vector2(xf, yf - 1.0) bottomRight = Vector2(xf - 1.0, yf) bottomLeft = Vector2(xf, yf) dotTopRight = dot(topRight, gradientTopRight) dotTopLeft = dot(topLeft, gradientTopLeft) dotBottomRight = dot(bottomRight, gradientBottomRight) dotBottomLeft = dot(bottomLeft, gradientBottomLeft)
În implementarea originală a algoritmului, Ken Perlin a folosit un tabel de permutări cu 256 de intrări pentru a determina ce vector gradient este asociat fiecărei intersecții a gridului. Astfel, vectorii gradient ajung să se repete în funcție de dimensiunea texturii pe care o generăm. Tot în implementarea originală, Ken Perlin a folosit următorul tabel de permutări, însă în practică se poate folosi orice alt tabel de permutări.
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 };
Odată generat tabelul de permutări, este nevoie de o valoare din acest tabel pentru fiecare dintre colțuri. Există însă o restricție: un colț trebuie să primească întotdeauna aceeași valoare, indiferent care dintre cele 4 celule de grid care îl pot avea drept colț conține valoarea de input. De exemplu, dacă colțul din dreapta sus al celulei gridului (0, 0) are o valoare de 42, atunci colțul din stânga sus al celulei gridului (1, 0) trebuie să aibă, de asemenea, aceeași valoare de 42. Este același punct al gridului, deci aceeași valoare indiferent de celula gridului.
X = floor(x) & 255 Y = floor(y) & 255 valueTopRight = permutations[permutations[X+1]+Y+1]; valueTopLeft = permutations[permutations[X]+Y+1]; valueBottomRight = permutations[permutations[X+1]+Y]; valueBottomLeft = permutations[permutations[X]+Y];
Odată asignată colțului valoarea din tabelul de permutări, trebuie calculat vectorul gradient constant al colțului. Pentru a simplifica lucrurile, putem folosi următoarea funcție:
def getConstantVector(v): h = v % 4 if h == 0: return Vector2(1.0, 1.0) elif h == 1: return Vector2(-1.0, 1.0) elif h == 2: return Vector2(-1.0, -1.0) elif h == 3: return Vector2(1.0, -1.0)
Odată calculate cele 4 produse scalare pentru cele 4 colțuri, acestea se vor interpola pentru a obține valoarea finală. Însă, la un moment dat se pot interpola doar 2 valori. Astfel, se pot interpola mai întâi valorile din stânga celului și cele din dreapta, iar apoi se va realiza o interpolare între cele 2 rezultate sau se pot interpola mai întâi valori din partea de jos a celulei și cele din partea de sus, iar apoi se vor interpola cele 2 valori rezultate.
def lerp(t, a1, a2): return a1 + t * (a2 - a1)
Dacă am folosi interpolarea liniară, aceasta nu ar da rezultate excelente, deoarece s-ar simți nenatural, trecerea de la o celulă la alta ar fi foarte bruscă:
În schimb, ne-am dori un rezultat de genul:
Pentru a obține de acest rezultat, Ken Perlin s-a folosit de funcția fade pentru a uniformiza valorile de input ale interpolării:
def fade(t): return ((6 * t - 15) * t + 10) * t * t * t