난수 hlsl


13

HLSL에서 난수를 어떻게 생성합니까?

gpu ray tracing 을 시도하고 있기 때문에 묻습니다 . 픽셀 셰이더에서 임의의 방향을 생성해야합니다. 따라서 randFloat()결과는 -1과 +1 사이의 임의의 숫자입니다.

또한 hlsl 노이즈 명령은 어떻게 처리됩니까? 문서 는 HLSL 2.0 이상에서 제거 되었다고 말합니다 . ?

임의의 값으로 텍스처를 채우는 방법에 대해 읽은 다음 해당 텍스처에 인덱스가있는 각 정점에 텍스처 좌표가 있습니다. 그러나 그것은 정점 당입니다 입니다. 픽셀 셰이더에서 호출 할 수있는 명령이 필요합니다. 또한이 방법은 매 프레임마다 다른 값을 원할 경우 버텍스 당 텍스 코드를 "재시 딩"해야하며 각 프레임마다 정점 버퍼 업데이트가 필요합니다 (비용이 많이들 수 있습니다).

자세하게, 어떻게 GPU에서 난수를 저렴하게 생성 할 수 있습니까?


1
버텍스 당 의미하는 것은, 그 텍스 코드는 픽셀 쉐이더에서 보간되고 그 픽셀 당 랜덤 텍스처에 대한 조회입니다.
키카이 마루

내 말은, 2 개의 텍스쳐 값이 너무 가까워지면이 의존성이 생길 것입니다 ( "블렌딩 된"값을 얻을 것입니다). 하나의 정점이 u, v에 대해 (0.5,0.5)를 가지고 있고 인접한 정점에 (0.51,0.52)가 있고 그 세그먼트에 500 개의 조각이 있다고 가정합니다. 그러면 출력은 2 또는 3 개의 실제 임의의 값이됩니다. 텍스처), 나머지는 얼굴을 따라 선형으로 보간되며 실제로는 무작위가 아닙니다. 그 가능성을 없애고 픽셀 쉐이더에서 호출 할 수있는 rand () 함수를 갖고 싶습니다
bobobobo

왜 하나의 꼭짓점에 0.5와 다른 0.51이 있는데, 무작위로 소리가 들리지 않으며,이 텍스 좌표를 "무작위 화"해야 할 필요가 있습니다. 원한다면 픽셀 쉐이더로 할 수 있지만 더 많은 작업이 필요합니다. . 이러한 텍스 코드는 자신의 위치에서 생성 될 필요가 없으므로 서로 얼마나 가까이 있는지는 중요하지 않습니다. 정점 셰이더에서 임의의 텍스처를 한 번 샘플링 할 수 있습니다 (위치, 법선, texcoords * texcoords로 일부 매개 변수 사용). 그러면 픽셀 셰이더에 전달할 texcoord가 제공됩니다.
Kikaimaru

@Kikaimaru 나는 ​​나쁜 상황에 의해 의미, 가능합니다 ..
bobobobo

무작위 순서로 서로 동일한 두 값을 가질 수 있음
Kikaimaru

답변:


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 스레드 당 하나 - 한 상태가 그대로 작은만큼 그 실행 가능한 (즉, 아마 만들 하지 메르 센 트위스터). 여러 SIMD 스레드가 협력하여 한 번에 여러 개의 난수를 생성 할 수있는 RNG 설계가 있습니까? 그것은 여기서 매우 유용 할 것입니다.
Nathan Reed

1
외부 링크가 깨진
조지 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();
    }
};
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.