如何在Metal着色器中获取随机数?

6

我该如何在Metal着色器中获取随机数?

我在《Metal Shading Language Specification》中搜索了“random”,但没有找到相关内容。

4个回答

8
所以我正在为另一个项目工作的随机数生成器,并希望把它打包成一个整洁的框架已经有一段时间了。您的问题促使我去做这件事。如果您不介意,这里是一个非常简单的框架,可以基于您提供的(最多)三个种子在金属着色器中为您生成随机数。该代码基于以下研究论文,该论文描述了如何在用于蒙特卡罗模拟的并行处理器上创建随机数。它还有一个(理论上的)周期为2^121,因此它应该适用于大多数可以在GPU上进行的合理计算。

您只需在着色器中调用初始化程序,然后调用rand()即可:

// Initialize a random number generator, seeds 2 and 3 are optional
Loki rng = Loki(seed1, seed2, seed3);

// get a random float [0,1)
float random_float = rng.rand();

我在存储库中还包含了一个示例项目,以便您可以看到它的使用方式。


6

看起来没有一个内置的函数。这个关于 MetalShaderShowcase/AAPLWoodShader.metal 的示例代码定义了它自己简单的rand函数。

// Generate a random float in the range [0.0f, 1.0f] using x, y, and z (based on the xor128 algorithm)
float rand(int x, int y, int z)
{
    int seed = x + y * 57 + z * 241;
    seed= (seed<< 13) ^ seed;
    return (( 1.0 - ( (seed * (seed * seed * 15731 + 789221) + 1376312589) & 2147483647) / 1073741824.0f) + 1.0f) / 2.0f;
}

对于 x、y、z 有什么要求吗?我是通过像素位置和系统生成噪声的。但是噪声看起来是周期性的,所以不太好。 - iaomw

2
请看一下[pcg-random],它非常简单和快速,更重要的是它很快。而且,将其C代码修改为Metal非常容易。https://www.pcg-random.org/
typedef struct { uint64_t state;  uint64_t inc; } pcg32_random_t;

void pcg32_srandom_r(thread pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
{
    rng->state = 0U;
    rng->inc = (initseq << 1u) | 1u;
    pcg32_random_r(rng);
    rng->state += initstate;
    pcg32_random_r(rng);
}

uint32_t pcg32_random_r(thread pcg32_random_t* rng)
{
    uint64_t oldstate = rng->state;
    rng->state = oldstate * 6364136223846793005ULL + rng->inc;
    uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
    uint32_t rot = oldstate >> 59u;
    return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}

我该如何使用它?

float randomF(thread pcg32_random_t* rng)
{
    //return pcg32_random_r(rng)/float(UINT_MAX);
    return ldexp(float(pcg32_random_r(rng)), -32);
}

pcg32_random_t rng;
pcg32_srandom_r(&rng, pos_grid.x*int_time, pos_grid.y*int_time);

auto randomFloat = randomF(&rng);

-1

不必在GPU上计算随机数,您也可以在CPU上计算一堆随机数,然后使用uniform / MTLBuffer将它们传递到着色器中。


我认为这个解决方案对于需要大量随机数的应用程序并不是非常有效... 那么在金属着色器中创建一个随机数生成器不是更好吗? - JoeVictor
1
@QuantumHoneybees,随机性必须来自某个地方。即使在Metal中实现了RNG,您也需要/希望种子来自CPU以获得初始的随机性。如果您将类似于顶点ID或片段位置之类的东西用作种子,则它不会在每次运行时随机。 - Ken Thomases
@KenThomases 嗯,如果我们从CPU传递一些随机性的东西,似乎最轻的传递方式是“帧计数器”,这样每次运行都会是随机的。几周前感谢您的帮助,我实际上能够创建一个非常好用的框架,可以在多个迭代中很好地工作。我很想听听您所有人的反馈!(: - JoeVictor
1
@QuantumHoneybees 嗯?如果你传递一个帧计数器,它将不会从一次运行到另一次运行是随机的。是的,它将在一次运行中从一个帧到另一个帧是随机的,但下一次运行将重新启动帧计数器,你只会重复所有其他运行相同的序列。 - Ken Thomases
@KenThomases 我误读了,我以为你指的是帧间。我的主要观点是只传递种子从 CPU 到 GPU,并在 GPU 上进行生成。您可以将时间作为种子简单地传递进去,这样它 在每次运行时都是随机的。 - JoeVictor
1
@QuantumHoneybees,当然,传递一个随机种子值是有意义的,这取决于你需要多少真正的随机性。但现在我们只是在争论从CPU发送多少随机数据。你仍然需要发送一些随机数据的原则仍然成立。而且,发送大量数据而不在GPU上生成任何数据可能是一个有效的选择。 - Ken Thomases

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接