随机数hlsl


13

您如何在HLSL中生成一个随机数?

我问是因为我想尝试gpu射线跟踪。您需要在像素着色器中生成随机方向。所以我想要randFloat(),结果是-1和+1之间的随机数。

另外,hlsl 噪声指令有何处理?文档说它已从HLSL 2.0及更高版本中删除为什么

我读到一种方法,其中用随机值填充纹理,然后在每个具有该纹理索引的顶点中都有一个纹理坐标。但这是每个顶点,我需要一条可以在像素着色器中调用的指令。另外,如果您希望每个帧使用不同的值,则此方法需要“重新播种”每个顶点的texcoord,并且需要每个帧都更新一个顶点缓冲区(这可能很昂贵!)

详细介绍一下,如何便宜地在GPU上生成随机数?


1
您所说的每个顶点是指,在像素着色器中插值了tex坐标,并对其随机纹理的每个像素进行了查找。
Kikaimaru

我的意思是,如果2个纹理值恰好彼此靠得太近,就会有这种依赖性(您将获得一个“混合”值)。假设一个顶点的u,v值为(0.5,0.5),相邻顶点的值为(0.51,0.52),并且在该线段上有500个片段..那么输出将是2或3个实际随机值(从纹理),但其余部分将沿着脸部进行线性插值,而并非真正随机。我想消除这种可能性,并拥有一个可以在像素着色器中调用的rand()函数
bobobobo 2012年

您为什么要说一个顶点有0.5,而另一个顶点有0.51,这听起来根本不是随机的,您还需要“随机化”这些tex坐标,如果需要,也可以在像素着色器中进行,但这要多得多。这些tex坐标不需要从它们的位置生成,因此它们彼此之间有多近也无所谓。您可以在顶点着色器中对随机纹理进行一次采样(使用位置,法线,texcoords *一些参数作为texcoords),这将为您提供传递给像素着色器的texcoords
Kikaimaru 2012年

@Kikaimaru我的意思是偶然的情况,有可能..
bobobobo

是的,以随机顺序出现,可能彼此之后具有两个相同的值
Kikaimaru

Answers:


14

像素着色器中的伪随机数不容易获得。在每次调用函数时,CPU上的伪随机数生成器都会具有某种状态,该状态既可以读取也可以写入。您不能在像素着色器中执行此操作。

这里有一些选择:

  1. 使用计算着色器而不是像素着色器-它们支持对缓冲区的读写访问,因此您可以实现任何标准PRNG。

  2. 根据屏幕空间位置等参数,从包含随机数据的一个或多个纹理中采样。您还可以在使用该位置查找纹理之前对位置进行一些数学运算,如果该数学运算涉及一个“每次绘制调用随机性”着色器常量,则应该重新使用纹理。

  3. 找到屏幕空间位置的一些数学函数,得出的结果“足够随机”。

快速的Google搜索发现此页面具有以下功能:

float rand_1_05(in float2 uv)
{
    float2 noise = (frac(sin(dot(uv ,float2(12.9898,78.233)*2.0)) * 43758.5453));
    return abs(noise.x + noise.y) * 0.5;
}

float2 rand_2_10(in float2 uv) {
    float noiseX = (frac(sin(dot(uv, float2(12.9898,78.233) * 2.0)) * 43758.5453));
    float noiseY = sqrt(1 - noiseX * noiseX);
    return float2(noiseX, noiseY);
}

float2 rand_2_0004(in float2 uv)
{
    float noiseX = (frac(sin(dot(uv, float2(12.9898,78.233)      )) * 43758.5453));
    float noiseY = (frac(sin(dot(uv, float2(12.9898,78.233) * 2.0)) * 43758.5453));
    return float2(noiseX, noiseY) * 0.004;
}

关于:计算着色器,您还可以将RNG状态存储在线程组共享内存中。为了提高效率,您不只需要一个 RNG状态(当每个线程都试图更新它时,争用将是一个巨大的瓶颈),而是很多-可能每个线程多达一个,或者每个4或8个线程一个,或者类似的-只要国家规模小到足以使之可行(即可能不是梅森·Twister)。是否有RNG设计,其中几个SIMD线程可以协作以一次生成多个随机数?这在这里非常有用。
内森·里德

1
外部链接已断开
George Birbilis

2

这是我如何使用计算着色器为粒子效果生成伪随机数。不确定此代码的随机性如何,但对于我的目的而言,它的效果足够好。

为每个GPU内核提供自己的随机序列的关键是从CPU向计算着色器提供初始随机种子。然后,每个内核都使用该种子以其线程ID作为计数循环数字生成器。从那里开始,每个线程都应该拥有自己的唯一种子以生成唯一值。

// Source
// http://www.gamedev.net/topic/592001-random-number-generation-based-on-time-in-hlsl/
// Supposebly from the NVidia Direct3D10 SDK
// Slightly modified for my purposes
#define RANDOM_IA 16807
#define RANDOM_IM 2147483647
#define RANDOM_AM (1.0f/float(RANDOM_IM))
#define RANDOM_IQ 127773u
#define RANDOM_IR 2836
#define RANDOM_MASK 123459876

struct NumberGenerator {
    int seed; // Used to generate values.

    // Returns the current random float.
    float GetCurrentFloat() {
        Cycle();
        return RANDOM_AM * seed;
    }

    // Returns the current random int.
    int GetCurrentInt() {
        Cycle();
        return seed;
    }

    // Generates the next number in the sequence.
    void Cycle() {  
        seed ^= RANDOM_MASK;
        int k = seed / RANDOM_IQ;
        seed = RANDOM_IA * (seed - k * RANDOM_IQ ) - RANDOM_IR * k;

        if (seed < 0 ) 
            seed += RANDOM_IM;

        seed ^= RANDOM_MASK;
    }

    // Cycles the generator based on the input count. Useful for generating a thread unique seed.
    // PERFORMANCE - O(N)
    void Cycle(const uint _count) {
        for (uint i = 0; i < _count; ++i)
            Cycle();
    }

    // Returns a random float within the input range.
    float GetRandomFloat(const float low, const float high) {
        float v = GetCurrentFloat();
        return low * ( 1.0f - v ) + high * v;
    }

    // Sets the seed
    void SetSeed(const uint value) {
        seed = int(value);
        Cycle();
    }
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.