Zufallszahl hlsl

13

Wie erzeugt man eine Zufallszahl in HLSL?

Ich frage, weil ich versuchen möchte, GPU-Raytracing . Sie müssen zufällige Richtungen in einem Pixel-Shader erzeugen. Ich möchte also randFloat(), dass das Ergebnis eine Zufallszahl zwischen -1 und +1 ist.

Wie ist der Umgang mit dem HLSL- Noise- Befehl? Die Dokumente sagen, dass es aus HLSL 2.0 und höher entfernt wurde. Warum ?

Ich habe über einen Ansatz gelesen, bei dem Sie eine Textur mit zufälligen Werten füllen und dann in jedem Scheitelpunkt eine Texturkoordinate haben, die einen Index für diese Textur enthält. Aber das ist per Vertex , ich brauche eine Anweisung, die ich im Pixel-Shader aufrufen kann. Außerdem erfordert dieser Ansatz das "Neueinordnen" der Texkoordinaten pro Scheitelpunkt, wenn Sie unterschiedliche Werte für jeden Frame wünschen, und dies erfordert ein Aktualisieren des Scheitelpunktpuffers für jeden Frame (was teuer sein kann!).

Wie kann man mit Detail billig eine Zufallszahl auf der GPU generieren?

Bobobobo
quelle
1
Was meinst du mit pro Scheitelpunkt? Diese Texkoordinate wird im Pixel-Shader interpoliert und die Suche pro Pixel wird auf diese zufällige Textur angewendet.
Kikaimaru
Was ich meine ist, wenn die 2 Texturwerte zu nahe beieinander liegen, wird es diese Abhängigkeit geben (Sie werden einen "gemischten" Wert erhalten). Angenommen, ein Scheitelpunkt hat (0,5,0,5) für u, v und der benachbarte Scheitelpunkt hat (0,51,0,52), und es gibt ungefähr 500 Fragmente in diesem Segment. Dann sind die Ausgaben 2 oder 3 tatsächliche Zufallswerte (von denen nachgeschlagen wird) Textur), aber der Rest wird linear entlang der Fläche interpoliert und nicht wirklich zufällig. Ich möchte diese Möglichkeit ausschließen und eine rand () -Funktion haben, die ich im Pixel-Shader
aufrufen
Warum würden Sie sagen, dass ein Vertex 0,5 und andere 0,51 hat, was überhaupt nicht zufällig klingt? Sie müssen auch diese Texkoordinaten "zufällig" machen. Sie können dies im Pixel-Shader tun, wenn Sie möchten, aber das sind viel mehr Operationen . Diese Tex-Coords müssen nicht aus ihrer Position generiert werden, es spielt also keine Rolle, wie nahe sie sich befinden. Sie können eine zufällige Textur einmal im Vertex-Shader abtasten (unter Verwendung von position, normal, texcoords * und einiger Parameter als texcoords). Auf diese Weise erhalten Sie Texcoords, die Sie an Pixel-Shader
Kikaimaru,
@ Kikaimaru Ich meine durch einen schlechten Zufall, es ist möglich ..
Bobobobo
Ja, in zufälliger Reihenfolge ist es möglich, zwei gleiche Werte
hintereinander

Antworten:

14

Pseudozufallszahlen in einem Pixel-Shader sind nicht einfach zu erhalten. Ein Pseudozufallszahlengenerator in der CPU hat bei jedem Aufruf der Funktion einen Zustand, aus dem er liest und in den er schreibt. In einem Pixel-Shader ist das nicht möglich.

Hier sind einige Optionen:

  1. Verwenden Sie einen Compute-Shader anstelle eines Pixel-Shaders. Sie unterstützen den Lese- und Schreibzugriff auf einen Puffer, sodass Sie jedes Standard-PRNG implementieren können.

  2. Stichprobe aus einer oder mehreren Texturen mit zufälligen Daten, basierend auf einem Parameter wie der Position des Bildschirmbereichs. Sie können auch einige Berechnungen an der Position durchführen, bevor Sie sie zum Nachschlagen in die Textur verwenden. Auf diese Weise können Sie die Textur wiederverwenden, wenn die Mathematik eine Shader-Konstante enthält, die nach dem Zufallsprinzip pro Draw aufgerufen wird.

  3. Finden Sie eine mathematische Funktion der Position des Bildschirmbereichs, die Ergebnisse liefert, die 'zufällig genug' sind.

Eine schnelle Google-Suche fand diese Seite mit diesen Funktionen:

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
quelle
Re: compute-Shader können Sie auch den RNG-Status im gemeinsamen Threadgruppen-Speicher speichern. Aus Gründen der Effizienz möchten Sie nicht nur einen RNG-Status (der Konflikt wäre ein enormer Engpass, da jeder Thread versucht hat, ihn zu aktualisieren), sondern viele - vielleicht sogar einen pro Thread oder einen pro 4 oder 8 Threads oder einen solchen - Solange die Staaten klein genug sind, um dies zu ermöglichen (dh wahrscheinlich nicht Mersenne Twister). Gibt es RNG-Designs, bei denen mehrere SIMD-Threads zusammenarbeiten können, um mehrere Zufallszahlen gleichzeitig zu generieren? Das wäre hier ganz nützlich.
Nathan Reed
1
Der externe Link ist kaputt
George Birbilis
2

So generiere ich mit einem Compute-Shader Pseudo-Zufallszahlen für meine Partikeleffekte. Ich bin mir nicht sicher, wie zufällig dieser Code ist, aber er funktioniert gut genug für meine Zwecke.

Der Schlüssel, um jedem GPU-Kernel seine eigene zufällige Sequenz zuzuweisen, besteht darin, dem Compute-Shader einen anfänglichen zufälligen Startwert von der CPU zuzuführen. Jeder Kernel verwendet dann diesen Startwert, um den Zahlengenerator unter Verwendung seiner Thread-ID als Zählung zu durchlaufen. Von dort sollte jeder Thread seinen eigenen eindeutigen Startwert haben, um eindeutige Werte zu generieren.

// 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
quelle