Mengapa Perlin Noise saya terlihat "ganjil"?

21

Saya mencoba menerapkan Perlin Noise sendiri menggunakan teori saja (mengikuti flafla2.github.io/2014/08/09/perlinnoise.html). Sayangnya saya tidak dapat mencapai tampilan Perlin Noise "asli".

Apa alasannya kode di bawah ini membuat versi Perlin Noise menjadi kuning?

Apa yang harus saya perbaiki / ubah dalam kode sehingga menjadikan Perlin Noise tanpa artefak?

Saya menduga mungkin ada masalah dalam cara saya interpolasi atau dalam gradsvektor. The gradsvektor berisi produk dot dari (vektor acak untuk titik kisi) dan (vektor ukuran) - untuk semua 4 poin terdekat kisi. (Vektor acak dan ukuran dijelaskan pada tautan pertama.)

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

Artefak dalam kebisingan

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
sumber

Jawaban:

24

Interpolasi terlihat baik-baik saja. Masalah utama di sini adalah bahwa fungsi hash yang Anda gunakan tidak terlalu baik. Jika saya melihat hanya satu oktaf, dan memvisualisasikan hasil hash dengan mengeluarkan hash(PT).x, saya mendapatkan sesuatu seperti ini:

fungsi hash buruk

Ini seharusnya benar-benar acak per kotak persegi, tetapi Anda dapat melihat bahwa ia memiliki banyak pola garis diagonal di dalamnya (hampir terlihat seperti kotak-kotak), jadi itu bukan hash yang sangat acak, dan pola-pola itu akan muncul di kebisingan yang dihasilkan olehnya.

Masalah lainnya adalah bahwa hash Anda hanya mengembalikan vektor gradien dalam [0, 1], sedangkan hash Anda harus dalam [−1, 1] untuk mendapatkan gradien ke segala arah. Bagian itu mudah diperbaiki dengan memetakan ulang.

Untuk memperbaiki masalah-masalah itu, saya beralih kode untuk menggunakan fungsi hash ini (yang saya pelajari dari Mikkel Gjoel, dan mungkin karena makalah oleh 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.;
}

Perhatikan bahwa karena fungsi trigonometri, ini akan sedikit lebih mahal daripada versi Anda. Namun, itu sangat meningkatkan penampilan dari kebisingan yang dihasilkan:

fbm noise dengan fungsi hash yang lebih baik

Nathan Reed
sumber
Terima kasih sangat banyak untuk penjelasan Anda. Ini mungkin di luar topik, tetapi saya akan tetap bertanya; dalam beberapa kode sumber yang menghitung kebisingan, orang menggunakan vektor vec3 (1, 57, 113) untuk menghitung titik produk dengan koordinat saat ini (saya kira tujuannya juga untuk mendapatkan hash). Mengapa pilihan konstanta ini (57 adalah sekitar 1 radian dalam derajat, 133 = sekitar 2 * radian dalam derajat)? Apakah karena periodisitas dalam fungsi trigonometri? Saya tidak dapat google ini.
sarasvati
3
@ Sarasvati Saya tidak begitu yakin, tetapi dugaan adalah bahwa 57 dan 113 dipilih karena mereka adalah bilangan prima. (113 adalah prima; 57 tidak, tapi itu 3 * 19, jadi masih agak primey ... kalau itu hal.) Mengalikan atau modding dengan nomor prime-ish cenderung mengacaukan bit, jadi itu bukan hal yang biasa. bahan dalam hash.
Nathan Reed
1
@cat Saya ragu GLSL memiliki PRNG, mengingat bahwa program GLSL bersifat deterministik.
user253751
1
Sepertinya ada beberapa kemungkinan pertanyaan baru di utas komentar ini ...
trichoplax
1
Saya memiliki artefak tersebut dan fungsi rand () ini memperbaikinya. Masalahnya adalah setelah saya berjalan sejauh 2 km di medan saya, artefak seperti OP mulai muncul lagi. Itu menggunakan fungsi hash di sini: amindforeverprogramming.blogspot.com/2013/07/... yang menyebabkan artefak hilang (Kecuali pada jarak 100 km, bc dari ketidaktepatan, tapi tidak apa-apa saya hanya perlu membagi menjadi potongan-potongan dan mendapatkan bahwa untuk bekerja dengan hashing kedua nilai, yang akan membiarkan perlin noise berjalan hampir tanpa batas). Jadi, saya akan meninggalkan ini di sini untuk mungkin membantu siapa saja yang memiliki masalah yang sama.
Nicholas Pipitone