Numero casuale hlsl


13

Come si genera un numero casuale in HLSL?

Lo sto chiedendo perché voglio provare a tracciare gpu ray . Devi generare direzioni casuali in un pixel shader. Quindi randFloat(), voglio , dove il risultato è un numero casuale compreso tra -1 e +1.

Inoltre, qual è il problema con l' istruzione hlsl noise ? I documenti dicono che è stato rimosso da HLSL 2.0 e versioni successive. Perché ?

Ho letto di un approccio in cui riempi una trama con valori casuali, quindi hai una coordinata trama in ciascun vertice che ha un indice in quella trama. Ma questo è per vertice , ho bisogno di un'istruzione che posso chiamare nel pixel shader. Inoltre, questo approccio richiede il "reseeding" dei texcoords per vertice se si desidera valori diversi per ogni frame e ciò richiede un aggiornamento del buffer dei vertici per ciascun frame (che può essere costoso!)

Con i dettagli, come è possibile generare in modo economico un numero casuale sulla GPU?


1
Cosa intendi per vertice, che tex coord viene interpolato in pixel shader e la sua ricerca per pixel in quella trama casuale.
Kikaimaru,

Quello che voglio dire è che se i 2 valori di trama risultano troppo vicini, ci sarà questa dipendenza (otterrai un valore "misto"). Supponiamo che un vertice abbia (0,5,0,5) per u, v e che il vertice adiacente abbia (0,51,0,52), e che ci siano 500 frammenti su quel segmento .. quindi le uscite saranno 2 o 3 valori casuali effettivi (cercati da trama), ma il resto sarà interpolato linearmente lungo la faccia e non veramente casuale. Voglio eliminare quella possibilità e avere una funzione rand () che posso chiamare nel pixel shader
bobobobo

Perché dovresti dire che un vertice ha 0,5 e altri 0,51, che non sembrano affatto casuali, devi anche "randomizzare" questi coordini tex, puoi farlo in pixel shader se vuoi, ma sono molte più operazioni . Quei cavi tex non devono essere generati dalla loro posizione, quindi non importa quanto vicini l'uno all'altro. Puoi campionare una trama casuale una volta nello shader di vertice (usando position, normal, texcoords * alcuni parametri come texcoords) e questo ti darà texcoords che passerai a pixel shader
Kikaimaru

@Kikaimaru Intendo per caso, è possibile ..
Bobobobo,

Sì in sequenza casuale è possibile avere due stessi valori uno dopo l'altro
Kikaimaru,

Risposte:


14

I numeri pseudo casuali in un pixel shader non sono facili da ottenere. Un generatore di numeri pseudo casuali sulla CPU avrà uno stato in cui legge e scrive, ad ogni chiamata alla funzione. Non puoi farlo in un pixel shader.

Ecco alcune opzioni:

  1. Utilizza uno shader di elaborazione anziché uno pixel: supportano l'accesso in lettura e scrittura a un buffer, in modo da poter implementare qualsiasi PRNG standard.

  2. Campione da una o più trame contenenti dati casuali, basato su un parametro come la posizione dello spazio dello schermo. Puoi anche fare alcuni calcoli sulla posizione prima di usarla per guardare in alto nella trama, il che dovrebbe permetterti di riutilizzare la trama se la matematica prevede una costante di shader per chiamata di richiamo.

  3. Trova alcune funzioni matematiche della posizione dello spazio dello schermo, che dia risultati 'abbastanza casuali'.

Una rapida ricerca su Google ha trovato questa pagina con queste funzioni:

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

Ri: calcola shader, puoi anche memorizzare lo stato RNG nella memoria condivisa di threadgroup. Per efficienza non vuoi solo uno stato RNG (la contesa sarebbe un enorme collo di bottiglia come ogni thread ha provato ad aggiornarlo), ma molti - forse fino a uno per thread, o forse uno per 4 o 8 thread o alcuni di questi - fintanto che gli stati sono abbastanza piccoli da renderlo fattibile (cioè probabilmente non Mersenne Twister). Esistono progetti RNG in cui più thread SIMD possono cooperare per generare più numeri casuali contemporaneamente? Sarebbe abbastanza utile qui.
Nathan Reed,

1
il collegamento esterno è interrotto
George Birbilis,

2

Ecco come posso generare numeri pseudo casuali per i miei effetti particellari con uno shader di calcolo. Non sono sicuro di quanto sia davvero casuale questo codice, ma funziona abbastanza bene per i miei scopi.

La chiave per dare a ciascun kernel GPU la propria sequenza casuale è alimentare allo shader di calcolo un seme casuale iniziale dalla CPU. Ogni kernel usa quindi questo seed per scorrere il generatore di numeri usando il suo id thread come conteggio. Da lì, ogni thread dovrebbe avere il proprio seme univoco per generare valori univoci.

// 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();
    }
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.