Pourquoi mon Perlin Noise a-t-il l'air "en bloc"?

21

J'ai essayé d'implémenter Perlin Noise par moi-même en utilisant uniquement la théorie (en suivant flafla2.github.io/2014/08/09/perlinnoise.html). Malheureusement, je n'ai pas pu obtenir l'apparence du bruit Perlin "original".

Quelle est la raison pour laquelle le code ci-dessous rend une version en blocs de Perlin Noise?

Que dois-je améliorer / changer dans le code pour qu'il rende Perlin Noise sans les artefacts?

Je soupçonne qu'il pourrait y avoir un problème dans la façon dont j'interpole ou dans le gradsvecteur. Le gradsvecteur contient des produits scalaires de (vecteur aléatoire pour le point de réseau) et (le vecteur de taille) - pour les 4 points de réseau à proximité. (Les vecteurs aléatoires et de taille sont décrits dans le tout premier lien.)

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

Artefacts dans le bruit

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
la source

Réponses:

24

L'interpolation semble bonne. Le principal problème ici est que la fonction de hachage que vous utilisez n'est pas très bonne. Si je regarde une seule octave et que je visualise le résultat du hachage en sortie hash(PT).x, j'obtiens quelque chose comme ceci:

mauvaise fonction de hachage

Ceci est censé être complètement aléatoire par carré de la grille, mais vous pouvez voir qu'il contient beaucoup de motifs de lignes diagonales (il ressemble presque à un damier), donc ce n'est pas un hachage très aléatoire, et ces motifs apparaîtront dans le bruit qu'il produit.

L'autre problème est que votre hachage ne renvoie que des vecteurs de gradient dans [0, 1], alors qu'ils devraient être dans [−1, 1] pour obtenir des gradients dans toutes les directions. Cette partie est facile à corriger en remappant.

Pour résoudre ces problèmes, j'ai changé le code pour utiliser cette fonction de hachage (que j'ai apprise de Mikkel Gjoel, et qui est probablement due à un article de WJJ Rey ):

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

Notez qu'en raison des fonctions trig, cela va être un peu plus cher que votre version. Cependant, il améliore considérablement l'apparence du bruit résultant:

bruit fbm avec une meilleure fonction de hachage

Nathan Reed
la source
Merci très beaucoup pour votre explication. C'est peut-être hors sujet, mais je vais quand même demander; dans certains codes sources qui calculent le bruit, les gens utilisent le vecteur vec3 (1, 57, 113) pour calculer le produit scalaire avec la coordonnée actuelle (je suppose que le but est également d'obtenir un hachage). Pourquoi ce choix particulier de constantes (57 est environ 1 radian en degrés, 133 = environ 2 * radian en degrés)? Est-ce à cause de la périodicité des fonctions trigonométriques? Je ne peux pas faire de recherche sur Google.
sarasvati
3
@sarasvati Je ne suis pas vraiment sûr, mais on suppose que 57 et 113 sont choisis parce que ce sont des nombres premiers. (113 est premier; 57 ne l'est pas, mais c'est 3 * 19, donc toujours un peu prime ... si c'est une chose.) La multiplication ou la modification par un nombre premier a tendance à brouiller les bits, donc ce n'est pas rare ingrédient dans les hachages.
Nathan Reed
1
@cat Je doute que GLSL ait un PRNG, étant donné que les programmes GLSL sont déterministes.
user253751
1
Il semble qu'il y ait plusieurs nouvelles questions potentielles dans ce fil de commentaires ...
trichoplax
1
J'avais ces artefacts et cette fonction rand () l'a corrigé. Le problème est qu'après avoir marché sur 2 km sur mon terrain, des artefacts comme les PO ont recommencé à apparaître. Il utilisait la fonction de hachage ici: amindforeverprogramming.blogspot.com/2013/07/… qui a fait disparaître les artefacts (sauf à des distances de 100 km, bc d'imprécision, mais ça va, je devais juste me séparer en morceaux et l'obtenir pour travailler en hachant les deux valeurs, ce qui permettra au bruit perlin de fonctionner presque indéfiniment). Donc, je vais laisser cela ici pour peut-être aider toute personne qui a le même problème.
Nicholas Pipitone