Dlaczego mój szum Perlin wygląda na „blokowy”?

21

Próbowałem wdrożyć Perlin Noise , używając tylko teorii (po flafla2.github.io/2014/08/09/perlinnoise.html). Niestety nie udało mi się uzyskać wyglądu „oryginalnego” hałasu Perlin.

Jaki jest powód, dla którego poniższy kod renderuje blokową wersję Perlin Noise?

Co powinienem ulepszyć / zmienić w kodzie, aby renderował szum Perlina bez artefaktów?

Podejrzewam, że może występować problem ze sposobem interpolacji lub gradswektora. ThegradsWektor zawiera dot produkty (losowy wektor do punktu kratowego) i (wektora size) - dla wszystkich 4 punktów pobliżu kratowych. (Wektory losowe i wielkości są opisane w pierwszym linku).

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

Artefakty w hałasie

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
źródło

Odpowiedzi:

24

Interpolacja wygląda dobrze. Głównym problemem jest to, że używana funkcja skrótu nie jest zbyt dobra. Jeśli spojrzę na tylko jedną oktawę i wizualizuję wynik mieszania poprzez wyjście hash(PT).x, otrzymam coś takiego:

zła funkcja skrótu

To ma być całkowicie losowe na kwadrat siatki, ale widać, że ma wiele ukośnych wzorów linii (prawie wygląda jak szachownica), więc nie jest to zbyt losowy skrót, a te wzory pojawią się w wytwarzany przez nią hałas.

Innym problemem jest to, że hash zwraca wektory gradientowe tylko w [0, 1], podczas gdy powinny znajdować się w [-1, 1], aby uzyskać gradienty we wszystkich kierunkach. Ta część jest łatwa do naprawienia poprzez ponowne mapowanie.

Aby rozwiązać te problemy, zmieniłem kod, aby użyć tej funkcji skrótu (której nauczyłem się od Mikkela Gjoela i prawdopodobnie wynika to z artykułu 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.;
}

Pamiętaj, że dzięki funkcjom wyzwalania będzie to nieco droższe niż Twoja wersja. Jednak znacznie poprawia wygląd wynikowego hałasu:

szum fbm z lepszą funkcją skrótu

Nathan Reed
źródło
Dziękuję bardzo za wyjaśnienie. To może nie na temat, ale i tak zapytam; w niektórych kodach źródłowych, które obliczają szum, ludzie używają wektora vec3 (1, 57, 113) do obliczania iloczynu z bieżącą współrzędną (przypuszczam, że celem jest również uzyskanie skrótu). Skąd ten konkretny wybór stałych (57 to ok. 1 radian w stopniach, 133 = ok. 2 * radian w stopniach)? Czy to z powodu okresowości w funkcjach trig? Nie mogę google google.
sarasvati
3
@ Sarasvati Nie jestem do końca pewien, ale przypuszczam, że 57 i 113 są wybrane, ponieważ są liczbami pierwszymi. (113 jest liczbą pierwszą; 57 nie jest, ale jest to 3 * 19, więc wciąż trochę prymitywne ... jeśli to jest coś.) Mnożenie lub modowanie przez liczbę pierwszą ma tendencję do mieszania bitów, więc nie jest to rzadkie składnik w haszach.
Nathan Reed
1
@cat Wątpię, czy GLSL ma PRNG, biorąc pod uwagę, że programy GLSL są deterministyczne.
user253751
1
Wygląda na to, że w tym wątku komentarza jest kilka potencjalnych nowych pytań ...
trichoplax
1
Miałem te artefakty i ta funkcja rand () to naprawiła. Problem polega na tym, że po przejściu 2 km po moim terenie artefakty, takie jak PO, zaczęły ponownie się pojawiać. Używał tutaj funkcji skrótu: amindforeverprogramming.blogspot.com/2013/07/..., która spowodowała, że ​​artefakty zniknęły (z wyjątkiem odległości 100 km, pne niedokładności, ale to w porządku, po prostu musiałem podzielić się na kawałki i dostałem to pracować, mieszając obie wartości, co pozwoli hałasowi perlin działać prawie bez końca). Zostawię to tutaj, aby pomóc każdemu, kto ma ten sam problem.
Nicholas Pipitone