Liczba losowa hlsl

13

Jak wygenerować liczbę losową w HLSL?

Pytam, ponieważ chcę spróbować śledzenia gpu ray . Musisz wygenerować losowe kierunki w module cieniującym piksele. Więc chcę randFloat(), gdzie wynikiem jest liczba losowa od -1 do +1.

Jaka jest umowa z instrukcją dotyczącą szumu hlsl ? Dokumenty mówią, że został on usunięty z HLSL 2.0 i nowszych wersji. Dlaczego ?

Czytałem o podejściu, w którym wypełniasz teksturę losowymi wartościami, a następnie masz współrzędną tekstury w każdym wierzchołku, który ma indeks do tej tekstury. Ale to na wierzchołek , potrzebuję instrukcji, którą mogę wywołać w module cieniującym piksele. Co więcej, to podejście wymaga „ponownego wysiewu” teksturordów na wierzchołek, jeśli chcesz mieć różne wartości w każdej ramce, a to wymaga aktualizacji bufora wierzchołków dla każdej ramki (co może być kosztowne!)

Jak szczegółowo możesz tanio wygenerować losową liczbę na GPU?

Bobobobo
źródło
1
Co rozumiesz przez „wierzchołek”, że koordynacja tekstów jest interpolowana w module cieniującym piksele i wyszukiwaniu poszczególnych pikseli na losową teksturę.
Kikaimaru,
Chodzi mi o to, że jeśli 2 wartości tekstury będą zbyt blisko siebie, będzie taka zależność (otrzymasz wartość „mieszaną”). Powiedzmy, że jeden wierzchołek ma (0,5,0,5) dla u, v, a sąsiedni wierzchołek ma (0,51,0,52), a na tym segmencie znajduje się około 500 fragmentów .. wtedy dane wyjściowe będą 2 lub 3 rzeczywistymi wartościami losowymi (patrząc od tekstury), ale reszta będzie interpolowana liniowo wzdłuż twarzy, a nie naprawdę przypadkowa. Chcę, aby wyeliminować tę możliwość i posiada funkcję rand (), że mogę zadzwonić w pixel shader
bobobobo
Dlaczego powiedziałbyś, że jeden wierzchołek ma 0,5, a drugi 0,51, który wcale nie brzmi losowo, musisz także „randomizować” te współrzędne tex, możesz to zrobić w shaderze pikseli, jeśli chcesz, ale to dużo więcej operacji . Te współrzędne tex nie muszą być generowane z ich pozycji, więc nie ma znaczenia, jak blisko są. Możesz raz próbkować losową teksturę w module cieniującym wierzchołki (używając position, normal, texcoords * niektóre parametry jako texcoords), a to da ci texcoords przekazywane do shadera pikseli
Kikaimaru
@Kikaimaru Mam na myśli przez zły przypadek, to możliwe ..
bobobobo
Tak w losowej sekwencji możliwe jest, aby mieć dwie takie same wartości po sobie
Kikaimaru

Odpowiedzi:

14

Pseudolosowe liczby w module cieniującym pikseli nie są łatwe do uzyskania. Generator liczb pseudolosowych w CPU będzie miał stan, z którego zarówno odczytuje, jak i zapisuje przy każdym wywołaniu funkcji. Nie możesz tego zrobić w module cieniującym piksele.

Oto kilka opcji:

  1. Użyj shadera obliczeniowego zamiast shadera pikselowego - obsługują one dostęp do odczytu i zapisu do bufora, dzięki czemu możesz wdrożyć dowolny standardowy PRNG.

  2. Próbka z jednej lub więcej tekstur zawierających losowe dane, na podstawie parametru takiego jak pozycja miejsca na ekranie. Możesz także wykonać matematykę dla danej pozycji przed użyciem jej do sprawdzenia tekstury, co powinno pozwolić ci na ponowne użycie tekstury, jeśli matematyka obejmuje stałą modułu cieniującego losowego na remis.

  3. Znajdź jakąś matematyczną funkcję położenia przestrzeni ekranu, która daje wyniki „wystarczająco losowe”.

Szybkie wyszukiwanie w Google znalazło tę stronę z tymi funkcjami:

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
źródło
Re: shadery obliczeniowe, można również przechowywać stan RNG w pamięci współużytkowanej grupy wątków. Dla wydajności nie potrzebujesz tylko jednego stanu RNG (spór byłby ogromnym wąskim gardłem, ponieważ każdy wątek próbowałby go zaktualizować), ale wielu - być może nawet jednego na wątek, a może jednego na 4 lub 8 wątków lub niektórych takich - dopóki stany są wystarczająco małe, aby było to wykonalne (tzn. prawdopodobnie nie jest to Mersenne Twister). Czy istnieją projekty RNG, w których kilka wątków SIMD może współpracować, generując jednocześnie kilka liczb losowych? Przydałoby się to tutaj.
Nathan Reed,
1
link zewnętrzny jest zepsuty
George Birbilis
2

Oto jak generuję pseudolosowe liczby dla moich efektów cząsteczkowych za pomocą modułu obliczającego. Nie jestem pewien, jak naprawdę losowy jest ten kod, ale działa wystarczająco dobrze dla moich celów.

Kluczem do nadania każdemu jądru GPU jego własnej losowej sekwencji jest podanie modułowi obliczeniowemu początkowego losowego ziarna z procesora. Każde jądro używa następnie tego ziarna do przełączania generatora liczb przy użyciu jego identyfikatora wątku jako liczby. Stamtąd każdy wątek powinien mieć własne unikalne ziarno, aby wygenerować unikalne wartości.

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