Nombre aléatoire hlsl

13

Comment générer un nombre aléatoire dans HLSL?

Je demande parce que je veux essayer le tracé de rayons gpu . Vous devez générer des directions aléatoires dans un pixel shader. Je veux donc randFloat(), où le résultat est un nombre aléatoire entre -1 et +1.

Aussi, quel est le problème avec l' instruction de bruit hlsl ? Les documents indiquent qu'il a été supprimé de HLSL 2.0 et versions ultérieures. Pourquoi ?

J'ai lu une approche où vous remplissez une texture avec des valeurs aléatoires, puis avez une coordonnée de texture dans chaque sommet qui a un index dans cette texture. Mais c'est par sommet , j'ai besoin d'une instruction que je peux appeler dans le pixel shader. De plus, cette approche nécessite de "réamorcer" les texcoords par sommet si vous voulez des valeurs différentes pour chaque trame, et cela nécessite une mise à jour du tampon de sommet chaque trame (ce qui peut être coûteux!)

Avec des détails, comment pouvez-vous générer à moindre coût un nombre aléatoire sur le GPU?

bobobobo
la source
1
Que voulez-vous dire par sommet, cette coordonnée tex est interpolée dans le pixel shader et sa recherche par pixel sur cette texture aléatoire.
Kikaimaru
Ce que je veux dire, c'est que si les 2 valeurs de texture sont trop proches l'une de l'autre, il y aura cette dépendance (vous obtiendrez une valeur "mixte"). Disons qu'un sommet a (0,5,0,5) pour u, v et le sommet adjacent a (0,51,0,52), et il y a comme 500 fragments sur ce segment .. alors les sorties seront 2 ou 3 valeurs aléatoires réelles (recherchées à partir de texture), mais le reste sera interpolé linéairement le long du visage et pas vraiment aléatoire. Je veux éliminer cette possibilité et avoir une fonction rand () que je peux appeler dans le pixel shader
bobobobo
Pourquoi diriez-vous qu'un sommet a 0,5 et l'autre 0,51, cela ne semble pas du tout aléatoire, vous devez également "randomiser" ces coords tex, vous pouvez le faire en pixel shader si vous le souhaitez, mais c'est beaucoup plus d'opérations . Ces coords tex n'ont pas besoin d'être générés à partir de leur position, peu importe à quel point ils sont proches les uns des autres. Vous pouvez échantillonner une texture aléatoire une fois dans le vertex shader (en utilisant position, normal, texcoords * certains paramètres comme texcoords) et cela vous donnera des texcoords que vous passez au pixel shader
Kikaimaru
@Kikaimaru Je veux dire par mauvais hasard, c'est possible ..
bobobobo
Oui, dans un ordre aléatoire, il est possible d'avoir deux mêmes valeurs l'une après l'autre
Kikaimaru

Réponses:

14

Les nombres pseudo aléatoires dans un pixel shader ne sont pas faciles à obtenir. Un générateur de nombres pseudo aléatoires sur le CPU aura un état dans lequel il lit et écrit à la fois, à chaque appel à la fonction. Vous ne pouvez pas faire cela dans un pixel shader.

Voici quelques options:

  1. Utilisez un nuanceur de calcul au lieu d'un nuanceur de pixels - ils prennent en charge l'accès en lecture-écriture à un tampon, de sorte que vous pouvez implémenter n'importe quel PRNG standard.

  2. Échantillon d'une ou plusieurs textures contenant des données aléatoires, basées sur un paramètre comme la position de l'espace à l'écran. Vous pouvez également faire quelques calculs sur la position avant de l'utiliser pour rechercher la texture, ce qui devrait vous permettre de réutiliser la texture si les calculs impliquent une constante de shader aléatoire par appel.

  3. Trouvez une fonction mathématique de la position de l'espace à l'écran, qui donne des résultats «assez aléatoires».

Une recherche rapide sur Google a trouvé cette page avec ces fonctions:

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
la source
Re: calculez les shaders, vous pouvez également stocker l'état RNG dans la mémoire partagée du groupe de threads. Pour plus d'efficacité, vous ne voulez pas un seul état RNG (la contention serait un énorme goulot d'étranglement car chaque thread a essayé de le mettre à jour), mais plusieurs - peut-être jusqu'à un par thread, ou peut-être un par 4 ou 8 threads ou certains autres - tant que les États sont suffisamment petits pour que cela soit possible (c'est-à-dire probablement pas Mersenne Twister). Existe-t-il des conceptions RNG dans lesquelles plusieurs threads SIMD peuvent coopérer pour générer plusieurs nombres aléatoires à la fois? Ce serait très utile ici.
Nathan Reed
1
le lien externe est rompu
George Birbilis
2

Voici comment je génère des nombres pseudo-aléatoires pour mes effets de particules avec un shader de calcul. Je ne sais pas à quel point ce code est vraiment aléatoire, mais il fonctionne assez bien pour mes besoins.

La clé pour donner à chaque noyau GPU sa propre séquence aléatoire est de fournir au shader de calcul une graine aléatoire initiale à partir du CPU. Chaque noyau utilise ensuite cette graine pour faire tourner le générateur de nombres en utilisant son identifiant de thread comme nombre. À partir de là, chaque thread doit avoir sa propre graine unique pour générer des valeurs uniques.

// 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
la source