Nomor acak hlsl

13

Bagaimana Anda menghasilkan angka acak di HLSL?

Saya bertanya karena saya ingin mencoba penelusuran gpu ray . Anda perlu membuat arah acak dalam shader piksel. Jadi saya ingin randFloat(), di mana hasilnya adalah angka acak antara -1 dan +1.

Juga, apa masalahnya dengan instruksi kebisingan hlsl ? Dokumen mengatakan itu dihapus dari HLSL 2.0 dan lebih tinggi. Mengapa ?

Saya membaca tentang pendekatan di mana Anda mengisi tekstur dengan nilai acak, kemudian memiliki koordinat tekstur di setiap titik yang memiliki indeks ke dalam tekstur itu. Tapi itu per vertex , saya butuh instruksi yang bisa saya panggil dalam pixel shader. Plus pendekatan ini membutuhkan "reseeding" texcoords per-vertex jika Anda ingin nilai yang berbeda setiap frame, dan yang memerlukan buffer vertex memperbarui setiap frame (yang bisa mahal!)

Dengan detail, bagaimana Anda bisa dengan murah menghasilkan angka acak pada GPU?

bobobobo
sumber
1
Apa yang Anda maksud dengan per vertex, bahwa tex coord akan diinterpolasi dalam pixel shader dan per pixel lookup ke tekstur acak itu.
Kikaimaru
Yang saya maksud adalah, jika 2 nilai tekstur kebetulan terlalu berdekatan, akan ada ketergantungan ini (Anda akan mendapatkan nilai "blended"). Katakanlah satu simpul memiliki (0,5,0.5) untuk u, v dan simpul yang berdekatan memiliki (0,51,0,52), dan ada seperti 500 fragmen pada segmen itu .. maka output akan menjadi 2 atau 3 nilai acak aktual (mendongak dari tekstur), tetapi sisanya akan diinterpolasi secara linier di sepanjang wajah dan tidak benar-benar acak. Saya ingin menghilangkan kemungkinan itu dan memiliki fungsi rand () yang dapat saya panggil dalam pixel shader
bobobobo
Mengapa Anda mengatakan bahwa satu simpul memiliki 0,5 dan lainnya 0,51, yang tidak terdengar acak sama sekali, Anda perlu "mengacak" tex coords ini juga, Anda dapat melakukannya dalam pixel shader jika Anda mau, tapi itu lebih banyak operasi . Koordinat tex tersebut tidak perlu dihasilkan dari posisi mereka sehingga tidak peduli seberapa dekat mereka satu sama lain. Anda dapat mencicipi tekstur acak satu kali dalam vertex shader (menggunakan posisi, normal, texcoords * beberapa parameter sebagai texcoords) dan ini akan memberi Anda texcoords yang Anda berikan ke pixel shader
Kikaimaru
@ Kikaimaru yang saya maksud dengan kejadian buruk, itu mungkin ..
bobobobo
Ya dalam urutan acak, mungkin memiliki dua nilai yang sama satu sama lain
Kikaimaru

Jawaban:

14

Angka acak semu dalam pixel shader tidak mudah diperoleh. Generator angka acak semu pada CPU akan memiliki beberapa status yang dibaca dan ditulis, pada setiap panggilan ke fungsi. Anda tidak dapat melakukannya dalam pixel shader.

Berikut beberapa opsi:

  1. Gunakan compute shader dan bukan pixel shader - mereka mendukung akses baca-tulis ke buffer, sehingga Anda dapat mengimplementasikan PRNG standar apa pun.

  2. Sampel dari satu atau lebih tekstur yang berisi data acak, berdasarkan parameter seperti posisi ruang layar. Anda juga dapat melakukan beberapa matematika pada posisi sebelum menggunakannya untuk melihat ke dalam tekstur, yang seharusnya memungkinkan Anda menggunakan kembali tekstur jika matematika melibatkan konstanta shader acak-per-gambar-panggilan.

  3. Temukan beberapa fungsi matematika dari posisi ruang layar, yang memberikan hasil yang 'cukup acak'.

Pencarian google cepat menemukan halaman ini dengan fungsi-fungsi ini:

float rand_1_05(in float2 uv)
{
    float2 noise = (frac(sin(dot(uv ,float2(12.9898,78.233)*2.0)) * 43758.5453));
    return abs(noise.x + noise.y) * 0.5;
}

float2 rand_2_10(in float2 uv) {
    float noiseX = (frac(sin(dot(uv, float2(12.9898,78.233) * 2.0)) * 43758.5453));
    float noiseY = sqrt(1 - noiseX * noiseX);
    return float2(noiseX, noiseY);
}

float2 rand_2_0004(in float2 uv)
{
    float noiseX = (frac(sin(dot(uv, float2(12.9898,78.233)      )) * 43758.5453));
    float noiseY = (frac(sin(dot(uv, float2(12.9898,78.233) * 2.0)) * 43758.5453));
    return float2(noiseX, noiseY) * 0.004;
}
Adam
sumber
Re: compute shaders, Anda juga bisa menyimpan status RNG di memori bersama threadgroup. Untuk efisiensi Anda tidak hanya menginginkan satu status RNG (pertengkaran akan menjadi hambatan besar karena setiap utas mencoba memperbaruinya), tetapi banyak - mungkin sebanyak satu per utas, atau mungkin satu per 4 atau 8 utas atau semacamnya - selama negara cukup kecil untuk membuatnya layak (yaitu mungkin bukan Mersenne Twister). Apakah ada desain RNG di mana beberapa thread SIMD dapat bekerja sama untuk menghasilkan beberapa nomor acak sekaligus? Itu akan sangat berguna di sini.
Nathan Reed
1
tautan eksternal terputus
George Birbilis
2

Inilah cara saya menghasilkan angka acak semu untuk efek partikel saya dengan penghitung komputasi. Tidak yakin seberapa benar acak kode ini, tetapi berfungsi cukup baik untuk tujuan saya.

Kunci untuk memberikan setiap kernel GPU urutan acak itu sendiri adalah untuk memberi makan penghitung benih awal acak dari CPU. Setiap kernel kemudian menggunakan seed ini untuk memutus generator nomor menggunakan id utas itu sebagai hitungan. Dari sana, setiap utas harus memiliki benih uniknya sendiri untuk menghasilkan nilai unik.

// Source
// http://www.gamedev.net/topic/592001-random-number-generation-based-on-time-in-hlsl/
// Supposebly from the NVidia Direct3D10 SDK
// Slightly modified for my purposes
#define RANDOM_IA 16807
#define RANDOM_IM 2147483647
#define RANDOM_AM (1.0f/float(RANDOM_IM))
#define RANDOM_IQ 127773u
#define RANDOM_IR 2836
#define RANDOM_MASK 123459876

struct NumberGenerator {
    int seed; // Used to generate values.

    // Returns the current random float.
    float GetCurrentFloat() {
        Cycle();
        return RANDOM_AM * seed;
    }

    // Returns the current random int.
    int GetCurrentInt() {
        Cycle();
        return seed;
    }

    // Generates the next number in the sequence.
    void Cycle() {  
        seed ^= RANDOM_MASK;
        int k = seed / RANDOM_IQ;
        seed = RANDOM_IA * (seed - k * RANDOM_IQ ) - RANDOM_IR * k;

        if (seed < 0 ) 
            seed += RANDOM_IM;

        seed ^= RANDOM_MASK;
    }

    // Cycles the generator based on the input count. Useful for generating a thread unique seed.
    // PERFORMANCE - O(N)
    void Cycle(const uint _count) {
        for (uint i = 0; i < _count; ++i)
            Cycle();
    }

    // Returns a random float within the input range.
    float GetRandomFloat(const float low, const float high) {
        float v = GetCurrentFloat();
        return low * ( 1.0f - v ) + high * v;
    }

    // Sets the seed
    void SetSeed(const uint value) {
        seed = int(value);
        Cycle();
    }
};
KlashnikovKid
sumber