Випадкове число hlsl


13

Як ви генеруєте випадкове число в HLSL?

Я запитую, бо хочу спробувати трасування gpu-променів . Вам потрібно генерувати випадкові напрямки в піксельній шейдері. Тому я хочу randFloat(), де результатом є випадкове число від -1 до +1.

Крім того, яка угода з інструкцією щодо шуму hlsl ? Документи кажуть, що він був видалений з HLSL 2.0 і вище. Чому ?

Я читав про підхід, коли ви заповнюєте текстуру випадковими значеннями, а потім маєте координату текстури у кожній вершині, яка має індекс на цю текстуру. Але це за вершиною , мені потрібна інструкція, яку я можу викликати в піксельному шейдері. Крім того, цей підхід вимагає "повторного перезавантаження" текстових рядків на вершину, якщо ви хочете, щоб кожен кадр відрізнявся значеннями, а для цього потрібен оновлення вершинного буфера для кожного кадру (що може бути дорого!)

Докладно, як можна дешево генерувати випадкове число на GPU?


1
Що ви маєте на увазі під вершиною, що координація tex отримує інтерполяцію в шейдері пікселів та її пошуку на піксель до цієї випадкової текстури.
Кікаймару

Що я маю на увазі, якщо два значення текстури виявляться занадто близькими між собою, існує така залежність (ви отримаєте "змішане" значення). Скажімо, одна вершина має (0,5,0,5) для u, v, а сусідня вершина має (0,51,0,52), і на цьому сегменті є 500 фрагментів .. тоді виходи будуть 2 або 3 фактичними випадковими значеннями текстура), але решта буде інтерпольованою лінійно вздовж обличчя і не справді випадковою. Я хочу усунути таку можливість і мати функцію rand (), яку я можу викликати в піксельній
шейдері

Чому б ви сказали, що одна вершина має 0,5, а інша 0,51, що зовсім не звучить випадково, вам також потрібно "рандомізувати" ці тек-кори, ви можете робити це в піксельному шейдері, якщо хочете, але це набагато більше операцій . Ці текстові координати не потрібно генерувати зі своєї позиції, тому не має значення, наскільки вони близькі один одному. Ви можете пробувати випадкову текстуру один раз у вершинній шейдері (використовуючи позицію, нормально, текстові слова * якийсь параметр як текстові слова), і це дасть вам текстові слова, які ви перейдете до піксельних шейдерів
Kikaimaru

@Kikaimaru Я маю на увазі поганий випадок, це можливо ..
bobobobo

Так, у випадковій послідовності можливо мати два однакових значення один за одним
Kikaimaru

Відповіді:


14

Псевдо випадкових чисел у піксельній шейдері отримати непросто. Генератор псевдовипадкових чисел у процесорі матиме певний стан, з якого він читає і записує під час кожного виклику функції. Ви не можете цього зробити в піксельній шейдері.

Ось кілька варіантів:

  1. Використовуйте обчислювальний шейдер замість піксельного шейдера - вони підтримують доступ для читання-запису до буфера, тому ви можете реалізувати будь-який стандартний PRNG.

  2. Вибірка з однієї або декількох текстур, що містять випадкові дані, на основі такого параметра, як положення на екрані. Ви також можете зробити деякі математичні позиції перед тим, як використовувати його для пошуку текстури, що дозволяє дозволити повторному використанню текстури, якщо математика передбачає постійну шейдерну константу на креслення дзвінка.

  3. Знайдіть певну математичну функцію положення простору екрану, яка дає результати, які є "досить випадковими".

Швидкий пошук Google знайшов цю сторінку з цими функціями:

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

Re: обчислюйте шейдери, ви також можете зберігати стан RNG в спільній пам'яті потокових груп. Для ефективності ви не бажаєте лише одного стану RNG (суперечка буде величезним вузьким місцем, оскільки кожна нитка намагалася оновити його), але багато - можливо, стільки, скільки один на нитку, або, можливо, один на 4 або 8 потоків або щось таке - до тих пір, поки штати досить малі, щоб зробити це здійсненним (тобто, мабуть, не Мерсен Твістер). Чи є проекти RNG, в яких кілька потоків SIMD можуть співпрацювати, щоб генерувати декілька випадкових чисел одночасно? Це було б тут дуже корисно.
Натан Рід

1
зовнішня ланка розірвана
Джордж Бірбіліс

2

Ось як я генерую псевдо випадкові числа для ефектів частинок за допомогою обчислювального шейдера. Не впевнений, наскільки справжньо випадковий цей код, але він працює досить добре для моїх цілей.

Ключовим фактором для надання ядра кожного GPU є власна випадкова послідовність - подача обчислювального шейдера початковим випадковим насінням із процесора. Кожне ядро ​​потім використовує це насіння для циклу генератора чисел, використовуючи його ідентифікатор потоку як кількість. Звідси кожна нитка повинна мати власне унікальне насіння для створення унікальних значень.

// 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();
    }
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.