Warum sieht mein Perlin-Rauschen „blockig“ aus?

21

Ich habe versucht, Perlin Noise selbst zu implementieren, indem ich nur die Theorie verwendet habe (folgt flafla2.github.io/2014/08/09/perlinnoise.html). Leider konnte ich den Look des "originalen" Perlin Noise nicht erreichen.

Was ist der Grund, warum der folgende Code eine blockartige Version von Perlin Noise darstellt?

Was muss ich im Code verbessern / ändern, damit Perlin Noise ohne die Artefakte wiedergegeben wird?

Ich vermute, dass es ein Problem in der Art und Weise gibt, wie ich interpoliere, oder im gradsVektor. Der gradsVektor enthält Punktprodukte von (Zufallsvektor für Gitterpunkte) und (Größenvektor) - für alle 4 nahegelegenen Gitterpunkte. (Die Zufalls- und Größenvektoren sind im allerersten Link beschrieben.)

GLSL Sandbox: http://glslsandbox.com/e#32663.0

Artefakte im Lärm

float fade(float t) { return t * t * t * (t * (t * 6. - 15.) + 10.); }
vec2 smooth(vec2 x) { return vec2(fade(x.x), fade(x.y)); }

vec2 hash(vec2 co) {
    return fract (vec2(.5654654, -.65465) * dot (vec2(.654, 57.4), co));
}

float perlinNoise(vec2 uv) {
    vec2 PT  = floor(uv);
    vec2 pt  = fract(uv);
    vec2 mmpt= smooth(pt);

    vec4 grads = vec4(
        dot(hash(PT + vec2(.0, 1.)), pt-vec2(.0, 1.)),   dot(hash(PT + vec2(1., 1.)), pt-vec2(1., 1.)),
        dot(hash(PT + vec2(.0, .0)), pt-vec2(.0, .0)),   dot(hash(PT + vec2(1., .0)), pt-vec2(1., 0.))
    );

    return 5.*mix (mix (grads.z, grads.w, mmpt.x), mix (grads.x, grads.y, mmpt.x), mmpt.y);
}

float fbm(vec2 uv) {
    float finalNoise = 0.;
    finalNoise += .50000*perlinNoise(2.*uv);
    finalNoise += .25000*perlinNoise(4.*uv);
    finalNoise += .12500*perlinNoise(8.*uv);
    finalNoise += .06250*perlinNoise(16.*uv);
    finalNoise += .03125*perlinNoise(32.*uv);

    return finalNoise;
}

void main() {
    vec2 position = gl_FragCoord.xy / resolution.y;
    gl_FragColor = vec4( vec3( fbm(3.*position) ), 1.0 );
}
sarasvati
quelle

Antworten:

24

Die Interpolation sieht gut aus. Das Hauptproblem hierbei ist, dass die von Ihnen verwendete Hash-Funktion nicht sehr gut ist. Wenn ich nur eine Oktave betrachte und das Hash-Ergebnis durch Ausgabe visualisiere hash(PT).x, erhalte ich ungefähr Folgendes:

schlechte Hash-Funktion

Dies sollte pro Gitterquadrat völlig zufällig sein, aber Sie können sehen, dass es viele diagonale Linienmuster enthält (es sieht fast aus wie ein Schachbrettmuster), so dass es sich nicht um einen sehr zufälligen Hash handelt, und diese Muster werden in angezeigt das Geräusch von ihm erzeugt.

Das andere Problem ist, dass Ihr Hash nur Gradientenvektoren in [0, 1] zurückgibt, während sie in [−1, 1] sein sollten, um Gradienten in alle Richtungen zu erhalten. Dieser Teil kann einfach durch Neuzuordnung behoben werden.

Um diese Probleme zu beheben, habe ich den Code auf die Verwendung dieser Hash-Funktion umgestellt (die ich von Mikkel Gjoel gelernt habe und die wahrscheinlich auf einem Artikel von WJJ Rey beruht ):

vec2 hash(vec2 co) {
    float m = dot(co, vec2(12.9898, 78.233));
    return fract(vec2(sin(m),cos(m))* 43758.5453) * 2. - 1.;
}

Beachten Sie, dass es aufgrund der Triggerfunktionen etwas teurer sein wird als Ihre Version. Es verbessert jedoch das Erscheinungsbild des resultierenden Geräusches erheblich:

fbm noise mit besserer hash funktion

Nathan Reed
quelle
Vielen Dank sehr viel für Ihre Erklärung. Dies ist vielleicht nicht zum Thema, aber ich werde trotzdem fragen; In einigen Quellcodes, die Rauschen berechnen, verwenden die Benutzer den Vektor vec3 (1, 57, 113), um das Skalarprodukt mit der aktuellen Koordinate zu berechnen (ich nehme an, das Ziel ist auch, einen Hash zu erhalten). Warum diese spezielle Auswahl von Konstanten (57 ist ungefähr 1 Bogenmaß in Grad, 133 = ungefähr 2 * Bogenmaß in Grad)? Liegt es an der Periodizität der Triggerfunktionen? Ich kann das nicht googeln.
Sarasvati
3
@sarasvati Ich bin mir nicht sicher, aber eine Vermutung ist, dass 57 und 113 gewählt werden, weil sie Primzahlen sind. (113 ist Primzahl, 57 nicht, aber es ist 3 * 19, also immer noch ein bisschen Primzahl ... wenn das eine Sache ist.) Das Multiplizieren oder Modifizieren mit einer Primzahl führt die Bits durcheinander, also ist es keine Seltenheit Zutat in Hashes.
Nathan Reed
1
@cat Ich bezweifle, dass GLSL ein PRNG hat, da GLSL-Programme deterministisch sind.
user253751
1
Es sieht so aus, als gäbe es in diesem Kommentarthread mehrere potenzielle neue Fragen ...
trichoplax
1
Ich hatte diese Artefakte und diese rand () - Funktion hat sie behoben. Das Problem ist, dass, nachdem ich 2 km in meinem Gelände gelaufen bin, Artefakte wie die OPs wieder auftauchten. Hier wurde die Hash-Funktion verwendet: amindforeverprogramming.blogspot.com/2013/07/… , die dazu führte , dass die Artefakte verschwanden arbeiten, indem beide Werte gehasht werden, wodurch das Perlin-Rauschen nahezu unbegrenzt weiterläuft). Also lasse ich das hier, um vielleicht jedem zu helfen, der das gleiche Problem hat.
Nicholas Pipitone