2
\$\begingroup\$

Using a C# script in Unity3D to control a HLSL compute shader, I'm trying to generate pseudo random numbers on the gpu. Following along with GPU Gems 3 Hybrid Tausworthe method and another thread Pseudo Random Number Generation on the GPU, I've come across an issue.

The problem:

the resulting texture appears to be one solid color. If I run the shader multiple times, I get a different solid color texture result every time, but the entire texture is the one color.

Compute shader code

#pragma kernel CSMain

RWTexture2D<float4> result; // 256 resolution texture to write to
uint4 randSeed;  //four uniform random numbers generated in 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(randSeed).value;
}

Do I need to save the state on the gpu? If so, how would I do that? Do I need to deallocate the memory afterwards?

I did try to assign the result of the HybridTaus() function to randSeed in hopes that it would use the new value in the following HybridTaus(randSeed) call, like this:

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

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

but to no avail.

\$\endgroup\$
3
\$\begingroup\$

You have to consider that one GPGPU invocation executes all CSMain in parallel, conceptually all at the same time.

So every pixel in your texture will come back with the same pseudo-random result as nothing changes in the values fed to the RNG.

Changing the seed value in the computer shader does not work: this is input-only and all CSMain execute in parallel so they all start with the same value. By the time the seed is changed it's too late. They are not sequentially executed.

To get "random" values in a shader you have to use some math formula and/or lookup texture both depending on the invocation coordinates (id.xy)

For example:

[numthreads(8, 8, 1)]
void CSMain(uint3 id)
{
    uint4 rs = randSeed;
    rs.x += id.x;
    rs.y += id.y;
    rs.z += (uint)(sin(id.x * 71.01) * 500461564);
    rs.w += (uint)(cos(id.y * 53.7) * 1023467329);
    result[id.xy] = HybridTaus(rs).value;
}

Using far fractional bits of sine/cosine can work really well as long as your factors are not a small integer ratio of PI.

\$\endgroup\$
0
\$\begingroup\$

First of all in result[id.xy] you must return a float4 (RGBA) and the second values must be between 0 and 1

Also, you will randomly color not every pixel but every part of the pixels that belongs to a certain stream id.xy

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.