乱数hlsl


13

HLSLで乱数を生成するにはどうすればよいですか?

gpuレイトレーシングを試したいので、私は尋ねています。ピクセルシェーダーでランダムな方向を生成する必要があります。したがってrandFloat()、結果は-1〜+1の乱数になります。

また、hlsl ノイズ命令との取引は何ですか?ドキュメントには、HLSL 2.0以降から削除されたと書かれています。なんで

テクスチャをランダムな値で満たし、そのテクスチャへのインデックスを持つ各頂点にテクスチャ座標を設定するアプローチについて読みました。しかし、それは頂点ごとです。ピクセルシェーダーで呼び出すことができる命令が必要です。さらに、このアプローチでは、フレームごとに異なる値が必要な場合は、頂点ごとのtexcoordsを「再シード」する必要があり、各フレームで頂点バッファーを更新する必要があります(高価になる可能性があります!)

詳細を説明すると、GPUで乱数を安価に生成する方法を教えてください。


1
頂点ごと、つまり、tex座標がピクセルシェーダーで補間され、ピクセルごとにそのランダムテクスチャがルックアップされるという意味です。
鬼丸

つまり、2つのテクスチャ値が近すぎると、この依存関係が発生します(「ブレンドされた」値が得られます)。u、vに対して1つの頂点に(0.5、0.5)があり、隣接する頂点に(0.51、0.52)があり、そのセグメントに500個のフラグメントがあるとすると、出力は2または3の実際のランダム値になります(テクスチャ)、しかし残りは顔に沿って直線的に補間され、真にランダムではありません。私はその可能性を排除し、私はピクセルシェーダで呼び出すことができるというのrand()関数を持つようにしたい
bobobobo

なぜ1つの頂点に0.5と他の0.51があり、それらはまったくランダムに聞こえない、これらのtex座標も「ランダム化」する必要があると言うのはなぜですか、必要に応じてピクセルシェーダーで行うことができますが、それはより多くの操作です。これらのtex座標は、その位置から生成する必要がないため、それらが互いにどれだけ近いかは関係ありません。頂点シェーダーでランダムテクスチャを1回サンプリングし(位置、法線、texcoords * texcoordsとしていくつかのパラメーターを使用)、これによりピクセルシェーダーに渡すtexcoordsが得られます
Kikaimaru

@Kikaimaru私は悪い出来事を意味します、それは可能です..
bobobobo

はい、ランダムなシーケンスで、2つの同じ値を
次々

回答:


14

ピクセルシェーダーの擬似乱数を取得するのは簡単ではありません。CPU上の疑似乱数ジェネレーターには、関数の呼び出しごとに読み取りと書き込みの両方の状態があります。ピクセルシェーダーではできません。

いくつかのオプションがあります:

  1. ピクセルシェーダーの代わりにコンピューティングシェーダーを使用します-バッファーへの読み取り/書き込みアクセスをサポートしているため、標準のPRNGを実装できます。

  2. スクリーンスペースの位置などのパラメーターに基づいて、ランダムデータを含む1つ以上のテクスチャのサンプル。また、テクスチャを検索するために使用する前に位置でいくつかの数学を行うことができます。これにより、数学が描画ごとのランダムシェーダ定数を含む場合、テクスチャを再利用できます。

  3. 「十分にランダムな」結果を与える、画面スペース位置の数学関数を見つけます。

グーグルで簡単に検索すると、次の機能を備えたこのページが見つかりました。

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

再:シェーダーを計算し、スレッドグループの共有メモリにRNG状態を保存することもできます。効率のために、1つの RNG状態(すべてのスレッドが更新しようとしたときに競合が大きなボトルネックになります)は必要ありませんが、多くの場合-スレッドごとに1つ、または4または8スレッドに1つなど-州がそれを実行可能にするのに十分小さい限り(つまり、おそらくMersenne Twisterではない)。複数のSIMDスレッドが連携して一度に複数の乱数を生成できるRNG設計はありますか?これは非常に便利です。
ネイサンリード

1
外部リンクが壊れています
ジョージビル

2

計算シェーダーを使用してパーティクルエフェクトの擬似乱数を生成する方法を次に示します。このコードが本当にランダムであるかはわかりませんが、私の目的には十分に機能します。

各GPUカーネルに独自のランダムシーケンスを与える鍵は、コンピューティングシェーダーにCPUからの初期ランダムシードを供給することです。次に、各カーネルはこのシードを使用して、スレッドIDをカウントとして使用して番号ジェネレーターを循環させます。そこから、各スレッドは、一意の値を生成するための独自の一意のシードを持つ必要があります。

// 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();
    }
};
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.