【问题标题】:Pseudo random number generation on the gpu在 gpu 上生成伪随机数
【发布时间】:2025-12-06 06:30:03
【问题描述】:

使用 Unity3D 游戏引擎中的 C# 脚本来控制 HLSL 计算着色器,我试图在 GPU 上生成伪随机数并将它们存储在 Texture2D 中。跟着一起 GPU Gems 3 Hybrid Tausworthe method 和另一个线程Pseudo Random Number Generation on the 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;
}

我需要将状态保存在 gpu 上吗?如果是这样,我该怎么做?之后需要释放内存吗?

我尝试将 HybridTaus() 函数的结果分配给 seed,希望它会在接下来的 HybridTaus(seed) 调用中使用新值,看看这是否会有所作为。我还尝试根据线程 ID(id 参数)添加唯一的任意数字。这给出了一些改进的结果,但我怀疑随机性只有我能做到的那样好,来自对线程 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;
}

【问题讨论】:

    标签: random gpu compute-shader


    【解决方案1】:

    首先,我不知道您发布的算法,但我在网上找到了this 简单算法,用于在 gpu 上生成随机数。这里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;
    }
    

    现在在大多数情况下这已经足够了,您可以传递计算着色器的本地调用 ID,因为它是唯一的,并为每个线程或每次调用获取一个随机数。但是,如果您每次调用都需要多个随机数(例如,您有一个循环或嵌套循环),则这不起作用,因为 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;
    }
    

    这里的xy 是我嵌套的for 循环变量。这对我有用。现在您可以在每次调用时获得多个随机数。

    但是,在您的情况下,我认为您不需要后者。如果我理解正确,您只需要为每个纹素存储一个随机数,这样您就可以尝试第一个并使用唯一的本地调用 ID 来获取每个纹素值的随机数。

    【讨论】: