如何在Metal Shader中获取随机数?

问题描述 投票:0回答:5

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

我在金属着色语言规范中搜索“随机”,但什么也没找到。

random metal
5个回答
9
投票

所以我正在为另一个项目开发随机数生成器,并希望将其打包成一个简洁的框架一段时间。

你的问题促使我这么做。如果您不介意无耻的插件,here是一个非常简单的框架,它将根据您提供的(最多)三个种子在金属着色器中为您生成随机数。该代码基于以下研究论文,该论文描述了如何在并行处理器上创建随机数以进行蒙特卡罗模拟。它还具有

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

2
投票

请看一下

[pcg-random]
,非常简单快捷,更重要的是速度快。修改 Metal 的 C 代码非常容易。 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);

0
投票

除了在 GPU 上计算随机数之外,您还可以在 CPU 上计算一堆随机数,然后使用统一 /

MTLBuffer
将它们传递到着色器中。


0
投票

基于乔的回答。我需要记住所有随机生成的数字,因此我需要通过 MTLComputeCommandEncoder 缓冲区将种子传递给金属着色器。如果有人感兴趣的话,这是代码。

获取随机 [0, 1] CPU 编号(我认为这是最快的方法):

CGFloat(drand48())

设置(在 AppDelegate 内)CPU 随机种子:

static func getRandomGeneratorSeed() -> Int
{
    Int(arc4random_uniform(UInt32(Int32.max)))
}

static func setRandomGeneratorSeed()
{
    let seed = Helper.getRandomGeneratorSeed()
    srand48(seed)
}

我有为每个金属着色器(我的效果/滤镜)调用的通用函数,所以我总是得到在 CPU 上计算的随机种子。

static func metalizationEffectSetParams1(
    computeEncoder: MTLComputeCommandEncoder,
    device: MTLDevice
)
{
    var seed = Float(Helper.getRandomGeneratorSeed())
    let seedBuffer = device.makeBuffer(length: MemoryLayout<Float>.size, options: [])
    let bufferPointer = seedBuffer?.contents()
    memcpy(bufferPointer, &seed, MemoryLayout<Float>.size)
    computeEncoder.setBuffer(seedBuffer, offset: 0, index: 0)
}

和金属着色器:

#import  "Loki/loki_header.metal"
#import  "MetalHelper_header.metal"

#include <metal_stdlib>
using namespace metal;

struct RandomSeed
{
    float seed;
};

// Lines 2
kernel void metalizationShader3(texture2d<float, access::read> inTexture [[texture(0)]],
                                texture2d<float, access::write> outTexture [[texture(1)]],
                                uint2 gid [[thread_position_in_grid]],
                                constant RandomSeed &rndStuff [[buffer(0)]]
                                ) {
    if (gid.x >= outTexture.get_width() || gid.y >= outTexture.get_height()) {
        return;
    }
    float4 color = inTexture.read(gid);
    float3 rndFloat3 = MetalHelper::randomFloat3(rndStuff.seed + gid.x - gid.y);
    float gray = dot(color.rgb, rndFloat3);
    outTexture.write(float4(gray, gray, gray, color.a), gid);
}

还有我的 MetalHelper.metal:

#import  "Loki/loki_header.metal"
#include "MetalHelper_header.metal"

#include <metal_stdlib>
using namespace metal;

thread float MetalHelper::random01(const unsigned seed) {
    return Loki(seed).rand();
}

thread float3 MetalHelper::randomFloat3(const unsigned seed)
{
    return float3(MetalHelper::random01(seed), MetalHelper::random01(seed), MetalHelper::random01(seed));
}

thread float MetalHelper::randomOneOrMinusOne(const unsigned seed)
{
    float r = MetalHelper::random01(seed);
    return r < 0.5 ? 1 : -1;
}
© www.soinside.com 2019 - 2024. All rights reserved.