Perlin-ljud
Perlin-brus är en typ av gradientbrus som utvecklades av Ken Perlin 1983. Det har många användningsområden, inklusive men inte begränsat till: procedurgenerering av terräng , applicering av pseudo-slumpmässiga förändringar på en variabel och hjälp med att skapa bildtexturer . Det är oftast implementerat i två, tre eller fyra dimensioner , men kan definieras för valfritt antal dimensioner.
Historia
Ken Perlin utvecklade Perlin-brus 1983 som ett resultat av sin frustration över det "maskinlika" utseendet av datorgenererade bilder (CGI) vid den tiden. Han beskrev formellt sina upptäckter i en SIGGRAPH -tidning 1985 kallad An Image Synthesizer . Han utvecklade den efter att ha arbetat på Disneys datoranimerade sci-fi- film Tron (1982) för animationsföretaget Mathematical Applications Group (MAGI) . År 1997 tilldelades Perlin en Oscar för teknisk prestation för att ha skapat algoritmen, vars citat löd:
Till Ken Perlin för utvecklingen av Perlin Noise, en teknik som används för att producera naturliga texturer på datorgenererade ytor för visuella effekter för rörliga bilder. Utvecklingen av Perlin Noise har gjort det möjligt för datorgrafiker att bättre representera komplexiteten hos naturfenomen i visuella effekter för filmindustrin.
Perlin ansökte inte om några patent på algoritmen, men 2001 beviljades han patent för användning av 3D+-implementeringar av simplexbrus för textursyntes . Simplex brus har samma syfte, men använder ett enklare utrymmesfyllande rutnät. Enkelt brus lindrar en del av problemen med Perlins "klassiska brus", bland annat beräkningskomplexitet och visuellt signifikanta riktningsartefakter.
Används
Perlin-brus är en primitiv procedurtextur , en typ av gradientbrus som används av konstnärer med visuella effekter för att öka intrycket av realism i datorgrafik . Funktionen har ett pseudo-slumpmässigt utseende, men alla dess visuella detaljer har samma storlek. Denna egenskap gör att den är lätt att kontrollera; Flera skalade kopior av Perlin-brus kan infogas i matematiska uttryck för att skapa en stor variation av processuella texturer. Syntetiska texturer som använder Perlin-brus används ofta i CGI för att få datorgenererade visuella element – såsom objektytor, eld, rök eller moln – att framstå mer naturliga genom att imitera det kontrollerade slumpmässiga utseendet av texturer i naturen.
Det används också ofta för att generera texturer när minnet är extremt begränsat, till exempel i demos . Dess efterföljare, såsom fraktalbrus och simplexbrus , har blivit nästan allestädes närvarande i grafikbehandlingsenheter både för realtidsgrafik och för procedurtexturer i icke-realtid i alla typer av datorgrafik.
Det används ofta i videospel för att skapa procedurgenererad terräng som ser naturlig ut.
Algoritm detalj
Perlin-brus är oftast implementerat som en två-, tre- eller fyrdimensionell funktion , men kan definieras för valfritt antal dimensioner. En implementering innefattar vanligtvis tre steg: definiera ett rutnät av slumpmässiga gradientvektorer, beräkna punktprodukten mellan gradientvektorerna och deras förskjutningar, och interpolation mellan dessa värden.
Grid definition
Definiera ett n -dimensionellt rutnät där varje rutkorsning har associerat med sig en fast slumpmässig n -dimensionell gradientvektor av enhetslängd, förutom i det endimensionella fallet där gradienterna är slumpmässiga skalärer mellan -1 och 1.
Punkt produkt
För att räkna ut värdet av en kandidatpoäng, hitta först den unika rutnätscellen där punkten ligger. Identifiera sedan de hörnen av den cellen och deras associerade gradientvektorer. Beräkna sedan en offsetvektor för varje hörn. En offsetvektor är en förskjutningsvektor från det hörnet till kandidatpunkten.
För varje hörn tar vi punktprodukten mellan dess gradientvektor och offsetvektorn till kandidatpunkten. Denna punktprodukt kommer att vara noll om kandidatpunkten är exakt i rutnätshörnet.
Observera att en gradientvektors inflytande växer med avståndet, vilket kan undvikas genom att normalisera offsetvektorn till en längd av först. Detta skulle införa märkbara skarpa förändringar, förutom att avståndet beaktas i följande interpolationssteg. Normalisering av offsetvektorn är dock inte en vanlig praxis.
För en punkt i ett tvådimensionellt rutnät kommer detta att kräva beräkning av 4 offsetvektorer och punktprodukter, medan det i tre dimensioner kommer att kräva 8 offsetvektorer och 8 punktprodukter. Generellt sett har algoritmen komplexitet , där är antalet dimensioner.
Interpolation
Det sista steget är interpolation mellan de punktprodukterna. Interpolation utförs med en funktion som har noll förstaderivata ( och möjligen även andraderivata) vid de rutnoderna. Därför, vid punkter nära rutnätsnoderna, kommer utsignalen att approximera punktprodukten av gradientvektorn för noden och offsetvektorn till noden. Detta innebär att brusfunktionen kommer att passera noll vid varje nod, vilket ger Perlin-bruset dess karakteristiska utseende.
Om , ett exempel på en funktion som interpolerar mellan värdet vid rutnätsnod 0 och värdet vid rutnätsnod 1 är
där smoothstep -funktionen användes.
Brusfunktioner för användning i datorgrafik producerar vanligtvis värden i området [-1,0,1,0] och kan skalas därefter.
Genomförande
Följande är en tvådimensionell implementering av Classical Perlin Noise, skriven i C.
Den ursprungliga referensimplementeringen av Perlin hade stora skillnader [ citat behövs ] :
- det använder en tredimensionell metod genom att interpolera mellan 8 hörn av en kub istället för de 4 hörnen i en kvadrat nedan.
- den slumpmässiga gradientriktningen blandar bitar av heltalskoordinaterna för hörn, vilket är mycket snabbare än att blanda med interferensen vid hög frekvens av rotationer av heltalskoordinaterna för hörn, slås samman och roteras igen med hög frekvens av en produkt: rotationerna är inte enhetliga distribuerad.
- Perlins metod delar upp heltalsrymden i 256x256x256 kuber och använder sedan en slumpmässig permutation av dessa kuber för att blanda dem, och sedan tilldelas varje kubpositionshörn en av tolv riktningar till de angränsande icke-permuterade kuberna i ett 4x4x4 beläggningsutrymme: detta kräver enbart beläggningsutrymme. heltalsoperationer men upprätthåller en enhetlig fördelning av riktningar.
- interpolationsfunktionen är det mjukare 4-graders Smoothersteget (med de tre första derivatorna lika med noll på klämgränserna) och inte det grundläggande linjära steget. Detta undviker synliga artefakter, särskilt längs hörn eller diagonaler som förenar provtagningshörnen, där resultatet skulle vara synligt anisotropt (att färga det önskade vita bruset till rosa brus ; om bruset användes för att generera en solid kristall skulle det inte vara helt svart och ogenomskinlig för ljuset, men delvis genomskinlig och färgad i vissa diskreta observationsriktningar).
#include <math.h> /* Funktion för att linjärt interpolera mellan a0 och a1 * Vikt w ska vara i intervallet [0.0, 1.0] */ float interpolate ( float a0 , float a1 , float w ) { /* // Du kanske vill klämma genom att infoga: * if (0,0 > w) returnera a0; * if (1,0 < w) returnera a1; */ return ( a1 - a0 ) * w + a0 ; /* // Använd denna kubiska interpolation [[Smoothstep]] istället för ett smidigt utseende: * return (a1 - a0) * (3,0 - w * 2,0) * w * w + a0; * * // Använd [[Smootherstep]] för ett ännu jämnare resultat med en andraderivata lika med noll på gränser: * retur (a1 - a0) * ((w * (w * 6,0 - 15,0) + 10,0) * w * w * w) + aO; */ } typedef struct { float x , y ; } vektor2 ; /* Skapa pseudoslumpmässig riktningsvektor */ vektor2 randomGradient ( int ix , int iy ) { // Inga förberäknade gradienter betyder att detta fungerar för valfritt antal rutnätskoordinater const unsigned w = 8 * sizeof ( unsigned ); const unsigned s = w / 2 ; // rotationsbredd utan tecken a = ix , b = iy ; a *= 3284157443 ; b ^= a << s | a >> w - s ; b *= 1911520717 ; a ^= b << s | b >> w - s ; a *= 2048419325 ; float slumpmässig = a * ( 3,14159265 / ~ ( ~ 0u >> 1 )); // i [0, 2*Pi] vektor2 v ; v . x = cos ( slumpmässigt ); v . y = sin ( slumpmässigt ); returnera v ; } // Beräknar punktprodukten av avstånds- och gradientvektorerna. float dotGridGradient ( int ix , int iy , float x , float y ) { // Få gradient från heltalskoordinater vektor2 gradient = randomGradient ( ix , iy ); // Beräkna avståndsvektorn float dx = x - ( float ) ix ; float dy = y - ( float ) iy ; // Beräkna dot-product return ( dx * gradient . x + dy * gradient . y ); } // Beräkna Perlin-brus vid koordinaterna x, y float perlin ( float x , float y ) { // Bestäm grid cell coordinates int x0 = ( int ) floor ( x ); int xl = x0 + 1 ; int y0 = ( int ) våning ( y ); int yl = y0 + 1 ; // Bestäm interpolationsvikter // Kan även använda högre ordnings polynom/s-kurva här float sx = x - ( float ) x0 ; float sy = y - ( float ) y0 ; // Interpolera mellan rutnätsgradienter flytande n0 , n1 , ix0 , ix1 , värde ; n0 = dotGridGradient ( x0 , y0 , x , y ); n1 = dotGridGradient ( x1 , y0 , x , y ); ix0 = interpolera ( no , nl , sx ); n0 = dotGridGradient ( x0 , y1 , x , y ); n1 = dotGridGradient ( x1 , y1 , x , y ); ixl = interpolera ( no , nl , sx ); värde = interpolera ( ix0 , ix1 , sy ); returvärde ; _ // Kommer tillbaka i intervallet -1 till 1. För att göra det i intervallet 0 till 1, multiplicera med 0,5 och lägg till 0,5 }
Permutation
Många implementeringar av Perlin-brus använder samma permutationsuppsättning som Ken Perlin använde i sin ursprungliga implementering. Den implementeringen är som följer:
int permutation [ ] = { 151 , 160 , 137 , 91 , 90 , 15 , 131 , 13 , 201 , 95 , 96 , 53 , 194 , 233 , 7 , 205 , 3 1 , 3 01 , 3 01 , 3 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 };
Denna specifika permutation är inte absolut nödvändig, även om den kräver en randomiserad uppsättning av värdena [0–255] (inklusive). Om man skapar en ny permutationstabell bör man se till att en enhetlig fördelning av värdena säkerställs.
Komplexitet
För varje utvärdering av brusfunktionen måste punktprodukten av positions- och gradientvektorerna utvärderas vid varje nod i den innehållande rutnätscellen. Perlin-brus skalas därför med komplexitet för dimensioner. Alternativ till Perlin-brus som ger liknande resultat med förbättrad komplexitetsskalning inkluderar simplexbrus och OpenSimplex-brus .
Se även
externa länkar
- Matt Zuckers Perlin noise math FAQ
- Rob Farbers handledning som demonstrerar Perlin-brusgenerering och visualisering på CUDA-aktiverade grafikprocessorer
- Jason Bevins omfattande C++-bibliotek för att generera komplexa, koherenta brusvärden
- PHP-implementering (GitHub)
- Perlin-brus förklarat på djupet (med C++-källkod)
- The Book of Shaders av Patricio Gonzalez Vivo & Jen Lowe
- Perlin brus online generator