Por que o meu ruído Perlin parece "irregular"?

21

Tentei implementar o Perlin Noise sozinho usando apenas a teoria (seguindo flafla2.github.io/2014/08/09/perlinnoise.html). Infelizmente, não consegui obter a aparência do "Perlin Noise" original.

Por que o código abaixo renderiza uma versão em bloco do Perlin Noise?

O que devo melhorar / alterar no código para gerar o Perlin Noise sem os artefatos?

Suspeito que possa haver algum problema na maneira como interpolo ou no gradsvetor. O gradsvetor contém produtos de ponto de (vetor aleatório para ponto de rede) e (o vetor de tamanho) - para todos os 4 pontos de rede próximos. (Os vetores aleatórios e de tamanho são descritos no primeiro link.)

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

Artefatos no ruído

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
fonte

Respostas:

24

A interpolação parece bem. O principal problema aqui é que a função de hash que você está usando não é muito boa. Se eu olhar apenas uma oitava e visualizar o resultado do hash através da saída hash(PT).x, obtenho algo assim:

função de hash ruim

Supõe-se que seja completamente aleatório por quadrado da grade, mas você pode ver que ele possui muitos padrões de linhas diagonais (quase parece um tabuleiro de xadrez); portanto, não é um hash muito aleatório, e esses padrões aparecerão em o barulho produzido por ele.

O outro problema é que seu hash retorna apenas vetores de gradiente em [0, 1], enquanto eles devem estar em [-1, 1] para obter gradientes em todas as direções. É fácil consertar essa parte remapeando.

Para corrigir esses problemas, mudei o código para usar essa função hash (que aprendi com Mikkel Gjoel e provavelmente se deve a um artigo 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.;
}

Observe que, devido às funções trigonométricas, será um pouco mais caro que a sua versão. No entanto, melhora consideravelmente a aparência do ruído resultante:

ruído fbm com melhor função hash

Nathan Reed
fonte
Obrigado muito muito para sua explicação. Talvez isso seja fora de tópico, mas perguntarei assim mesmo; em alguns códigos-fonte que calculam ruído, as pessoas usam o vetor vec3 (1, 57, 113) para calcular o produto escalar com a coordenada atual (suponho que o objetivo também seja obter um hash). Por que essa escolha específica de constantes (57 é de aproximadamente 1 radiano em graus, 133 = aproximadamente 2 * radiano em graus)? É por causa da periodicidade nas funções trigonométricas? Não consigo pesquisar no Google.
22416 sarasvati
3
@sarasvati Não tenho muita certeza, mas acho que 57 e 113 são escolhidos porque são números primos. (113 é primo; 57 não é, mas é 3 * 19, então ainda é meio ... se isso é uma coisa.) Multiplicar ou modificar por um número primo tende a confundir os bits, por isso não é incomum ingrediente em hashes.
Nathan Reed
1
@cat Duvido que o GLSL tenha um PRNG, já que os programas GLSL são determinísticos.
User253751
1
Parece que existem vários potenciais novas perguntas neste segmento comentário ...
Trichoplax
1
Eu estava tendo esses artefatos e essa função rand () o corrigiu. O problema é que, depois que andei 2 km no meu terreno, artefatos como os OPs começaram a aparecer novamente. Ele estava usando a função hash aqui: amindforeverprogramming.blogspot.com/2013/07/… que causou o desaparecimento dos artefatos (exceto a distâncias de 100 km, bc de imprecisão, mas tudo bem, só tive que dividir em pedaços e consegui isso para trabalhar com hash dos dois valores, o que permitirá que o ruído perlin seja executado indefinidamente). Então, deixarei isso aqui para ajudar talvez quem tiver o mesmo problema.
Nicholas Pipitone 27/10