Генерация псевдослучайных чисел на графическом процессоре

Используя сценарий C# в игровом движке Unity3D для управления вычислительным шейдером HLSL, я пытаюсь сгенерировать псевдослучайные числа на GPU и сохранить их в Texture2D. Следуя методу Geus 3 Hybrid Tausworthe на GPU и другому потоку генерации псевдослучайных чисел на GPU, я столкнулся с проблемой.

Эта проблема:

Полученная текстура выглядит как один сплошной цвет. Если я запускаю шейдер несколько раз, каждый раз получаю разные текстуры сплошного цвета, но вся текстура одного цвета.

Вычислить код шейдера

#pragma kernel CSMain

RWTexture2D<float4> result; // 256 resolution texture to write to
uint4 seed;  //four uniform random numbers generated on the CPU in a C# script

struct RandomResult
{
    uint4 state;
    float value;
};

uint TausStep(uint z, int S1, int S2, int S3, uint M)
{
    uint b = (((z << S1) ^ z) >> S2);
    return ((z & M) << S3) ^ b;
}
uint LCGStep(uint z, uint A, uint C)
{
    return A * z + C;
}
RandomResult HybridTaus(uint4 state)
{
    state.x = TausStep(state.x, 13, 19, 12, 4294967294);
    state.y = TausStep(state.y, 2, 25, 4, 4294967288);
    state.z = TausStep(state.z, 3, 11, 17, 4294967280);
    state.w = LCGStep(state.w, 1664525, 1013904223);

    RandomResult rand;
    rand.state = state;
    rand.value = 2.3283064365387e-10 * (state.x ^ state.y ^ state.z ^ state.w);

    return rand;
}

[numthreads(8, 8, 1)]
void CSMain(uint3 id)
{
    result[id.xy] = HybridTaus(seed).value;
}

Нужно ли сохранять состояние на графическом процессоре? Если так, как бы я это сделал? Нужно ли освобождать память потом?

Я попытался присвоить результат функции HybridTaus() seed в надежде на то, что он будет использовать новое значение в следующем вызове HybridTaus(seed), чтобы увидеть, будет ли это иметь значение. Я также попытался добавить уникальные произвольные числа на основе идентификатора потока, который является id параметр. Это дало некоторые улучшенные результаты, но я подозреваю, что случайность настолько хороша, насколько я могу это сделать, исходя из математических операций, выполненных на идентификаторах потоков, а не эффективно из генератора случайных чисел.

[numthreads(8, 8, 1)]
void CSMain(uint3 id)
{
    //first thing I tried
    //RandomResult rand = HybridTaus(seed);
    //seed = rand.state;  // re-assign seed with the new state

    //result[id.xy] = rand.value;

    //second thing I tried
    RandResult rand = HybridTaus(seed * uint4(id.x*id.y*id.x*id.y,
                                              id.x*id.y/id.x*id.y, 
                                              id.x*id.y+id.x*id.y,
                                              id.x*id.y-id.x*id.y));
    result[id.xy] = rand.value;
}

1 ответ

Во-первых, я не знаю, какой алгоритм вы опубликовали, но я нашел этот простой алгоритм онлайн для генерации случайных чисел в графическом процессоре. Вот seed это 32 бит uint,

uint wang_hash(uint seed)
{
    seed = (seed ^ 61) ^ (seed >> 16);
    seed *= 9;
    seed = seed ^ (seed >> 4);
    seed *= 0x27d4eb2d;
    seed = seed ^ (seed >> 15);
    return seed;
}

Теперь в большинстве случаев этого достаточно, вы можете передать идентификатор локального вызова вашего вычислительного шейдера как уникальный и получить случайное число для потока или для вызова. Однако если вам нужно несколько случайных чисел на вызов (например, у вас есть цикл или вложенный цикл), это не сработает как seed остается такой же. Так что я немного испортил функцию и придумал

uint wang_hash(uint seed)
{
    seed = seed + 76.897898 * 48.789789 *  cos(x) * sin(y) * 20.79797
    seed = (seed ^ 61) ^ (seed >> 16);
    seed *= 9;
    seed = seed ^ (seed >> 4);
    seed *= 0x27d4eb2d;
    seed = seed ^ (seed >> 15);
    return seed;
}

Вот x а также y мои вложенные в циклы переменные. И это работает для меня. Теперь вы можете получить несколько случайных чисел за вызов.

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

Другие вопросы по тегам