Rastgele sayı hlsl


13

HLSL'de rastgele bir sayıyı nasıl üretiyorsunuz?

Soruyorum çünkü gpu ray izlemeyi denemek istiyorum . Bir piksel gölgelendiricide rastgele yönler oluşturmanız gerekir. randFloat()Sonuç istiyorum -1 ile +1 arasında rastgele bir sayı.

Ayrıca, hlsl gürültü talimatı ile anlaşma nedir? Dokümanlar , HLSL 2.0 ve sonraki sürümlerden kaldırıldığını söylüyor . Neden ?

Bir dokuyu rastgele değerlerle doldurduğunuz, ardından her dokuda o dokuya bir indeksi olan bir doku koordinatına sahip olduğunuz bir yaklaşımı okudum. Ama bu köşe başına , piksel gölgelendiricisinde çağırabileceğim bir talimata ihtiyacım var. Ayrıca bu yaklaşım, her karede farklı değerler istiyorsanız köşe başına tekerlerin "yeniden tohumlanmasını" gerektirir ve bu da her karede bir köşe arabelleği güncellemesi gerektirir (bu pahalı olabilir!)

Ayrıntılı olarak, GPU'da nasıl ucuza rastgele bir sayı oluşturabilirsiniz?


1
Köşe başına ne demek istersiniz, bu metin koordinat piksel gölgeleyicisinde enterpolasyona girer ve piksel başına bu rastgele dokuya bakar.
Kikaimaru

Demek istediğim, eğer 2 doku değeri birbirine çok yakın olursa, bu bağımlılık olacaktır ("harmanlanmış" bir değer elde edersiniz). Bir tepe noktasının u, v için (0.5,0.5) olduğunu ve bitişik tepe noktasının (0.51,0.52) olduğunu ve bu segmentte 500'e yakın parça olduğunu varsayalım. O zaman çıktılar 2 veya 3 gerçek rastgele değer olacaktır ( doku), ancak geri kalanı enterpolasyonlu yüz boyunca doğrusal olacak ve gerçekten rastgele değil. Bu olasılığı ortadan kaldırmak ve piksel gölgeleyicisinde çağırabileceğim bir rand () işlevine sahip olmak istiyorum
bobobobo 20:12

Neden bir tepe noktasının 0,5 ve diğer 0,51'e sahip olduğunu, bu hiç de rastgele gelmediğini söylüyorsunuz, bu tex koordinatlarını da "rastgele" yapmanız gerekiyor, isterseniz piksel gölgelendiricisinde yapabilirsiniz, ancak bu çok daha fazla işlem . Bu tex koordinatlarının konumlarından üretilmelerine gerek yoktur, bu yüzden birbirlerine ne kadar yakın oldukları önemli değildir. Tepe gölgelendiricisinde bir kez rastgele doku örnekleyebilirsiniz (konum, normal, texcoord * * texcoords olarak bazı parametreler) ve bu size piksel gölgelendiriciye ilettiğiniz texcoordları verecektir
Kikaimaru

@Kikaimaru Yani kötü bir olay, mümkün ..
bobobobo

Evet rastgele sırada birbiri ardına iki aynı değere sahip olmak mümkün
Kikaimaru

Yanıtlar:


14

Bir piksel gölgelendiricideki sahte rasgele sayılar elde etmek kolay değildir. CPU üzerindeki bir sözde rasgele sayı üreteci, işleve yapılan her çağrıda hem okuduğu hem de yazdığı bir duruma sahip olacaktır. Bunu bir piksel gölgeleyicisinde yapamazsınız.

İşte bazı seçenekler:

  1. Piksel gölgelendirici yerine bir hesaplama gölgelendiricisi kullanın - bir arabelleğe okuma-yazma erişimini destekler, böylece herhangi bir standart PRNG'yi uygulayabilirsiniz.

  2. Ekran alanı konumu gibi bir parametreye dayalı olarak rastgele veri içeren bir veya daha fazla dokudan örnek alın. Ayrıca, dokuya bakmak için kullanmadan önce pozisyonda bazı matematik işlemleri de yapabilirsiniz; bu, matematik çağrı başına rastgele bir gölgelendirici sabiti içeriyorsa dokuyu yeniden kullanmanıza izin vermelidir.

  3. Ekran alanı konumunun matematiksel fonksiyonunu bulun ve bu da 'yeterince rasgele' sonuçlar verir.

Hızlı bir google araması bu sayfayı şu işlevlerle buldu:

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;
}

Re: shader hesaplamak, ayrıca RNG durumunu threadgroup paylaşılan bellekte saklayabilirsiniz. Verimlilik için sadece bir RNG durumu istemezsiniz (çekişme, her iş parçacığı güncellemeye çalıştığı kadar büyük bir darboğaz olacaktır), ancak birçoğu - belki de iş parçacığı başına bir tane veya belki de 4 veya 8 iş parçacığı veya bir tanesi için - Devletler bunu mümkün kılacak kadar küçük olduğu sürece (yani muhtemelen Mersenne Twister değil ). Birkaç SIMD iş parçacığının aynı anda birkaç rasgele sayı üretmek için işbirliği yapabileceği RNG tasarımları var mı? Burada oldukça faydalı olur.
Nathan Reed

1
dış bağlantı kopuk
George Birbilis

2

İşte bir hesaplama gölgelendiricisi ile parçacık efektlerim için sahte rasgele sayılar üretiyorum. Bu kod ne kadar gerçekten rasgele emin değilim ama benim amaçlar için yeterince iyi çalışıyor.

Her GPU çekirdeğine kendi rastgele sırasını vermenin anahtarı, hesaplama gölgelendiricisine CPU'dan bir ilk rastgele tohum beslemektir. Her çekirdek daha sonra bu çekirdeği, sayı kimliğini sayı olarak iş parçacığı kimliğini kullanarak döngü yapmak için kullanır. Oradan, her iş parçacığının benzersiz değerler üretmek için kendine özgü bir tohumu olmalıdır.

// 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();
    }
};
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.