¿Por qué mi ruido Perlin se ve "bloqueado"?

21

Traté de implementar Perlin Noise solo usando la teoría (siguiendo flafla2.github.io/2014/08/09/perlinnoise.html). Lamentablemente, no pude lograr el aspecto del ruido "original" de Perlin.

¿Cuál es la razón por la que el siguiente código muestra una versión en bloque de Perlin Noise?

¿Qué debería mejorar / cambiar en el código para que genere ruido Perlin sin los artefactos?

Sospecho que podría haber un problema en la forma en que interpolo o en el gradsvector. El gradsvector contiene productos de punto de (vector aleatorio para el punto de red) y (el vector de tamaño) - para los 4 puntos de red cercanos. (Los vectores aleatorios y de tamaño se describen en el primer enlace).

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

Artefactos en el ruido

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
fuente

Respuestas:

24

La interpolación se ve bien. El principal problema aquí es que la función hash que estás utilizando no es muy buena. Si miro solo una octava y visualizo el resultado de hash al generar hash(PT).x, obtengo algo como esto:

mala función hash

Se supone que esto es completamente aleatorio por cuadrado de la cuadrícula, pero puede ver que tiene muchos patrones de líneas diagonales (casi parece un tablero de ajedrez), por lo que no es un hash muy aleatorio, y esos patrones aparecerán en El ruido que produce.

El otro problema es que su hash solo devuelve vectores de gradiente en [0, 1], mientras que deberían estar en [−1, 1] para obtener gradientes en todas las direcciones. Esa parte es fácil de arreglar reasignando.

Para solucionar esos problemas, cambié el código para usar esta función hash (que aprendí de Mikkel Gjoel, y probablemente se deba a un documento 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.;
}

Tenga en cuenta que debido a las funciones trigonométricas será un poco más caro que su versión. Sin embargo, mejora considerablemente la apariencia del ruido resultante:

ruido fbm con mejor función hash

Nathan Reed
fuente
Gracias muy mucho por su explicación. Esto quizás esté fuera de tema, pero lo preguntaré de todos modos; En algunos códigos fuente que calculan el ruido, las personas usan el vector vec3 (1, 57, 113) para calcular el producto de punto con la coordenada actual (supongo que el objetivo también es obtener un hash). ¿Por qué esta elección particular de constantes (57 es aproximadamente 1 radiano en grados, 133 = aproximadamente 2 * radianes en grados)? ¿Es por la periodicidad en las funciones trigonométricas? No puedo googlear esto.
sarasvati
3
@sarasvati No estoy realmente seguro, pero supongo que se eligen 57 y 113 porque son números primos. (113 es primo; 57 no lo es, pero es 3 * 19, así que todavía es un poco primo ... si eso es algo). Multiplicar o modificar por un número primo tiende a confundir los bits, por lo que no es raro ingrediente en hashes.
Nathan Reed
1
@cat Dudo que GLSL tenga un PRNG, dado que los programas GLSL son deterministas.
user253751
1
Parece que hay varias posibles nuevas preguntas en este hilo de comentarios ...
trichoplax
1
Estaba teniendo esos artefactos y esta función rand () lo arregló. El problema es que después de caminar 2 km en mi terreno, los artefactos como los OP comenzaron a aparecer nuevamente. Estaba usando la función hash aquí: amindforeverprogramming.blogspot.com/2013/07/... que hizo que los artefactos desaparecieran (Excepto a distancias de 100 km, antes de la imprecisión, pero eso está bien, solo tuve que dividirme en trozos y obtuve eso para trabajar haciendo hash ambos valores, lo que permitirá que el ruido perlin corra casi indefinidamente). Entonces, dejaré esto aquí para ayudar a cualquiera que tenga el mismo problema.
Nicholas Pipitone