Número aleatório hlsl

13

Como você gera um número aleatório no HLSL?

Estou perguntando porque quero tentar o rastreamento de raios gpu . Você precisa gerar direções aleatórias em um pixel shader. Então, eu quero randFloat(), onde o resultado é um número aleatório entre -1 e +1.

Além disso, qual é o problema da instrução hlsl noise ? Os documentos dizem que foi removido do HLSL 2.0 e superior. Por que ?

Eu li sobre uma abordagem em que você preenche uma textura com valores aleatórios e, em seguida, tem uma coordenada de textura em cada vértice que possui um índice nessa textura. Mas isso é por vértice , preciso de uma instrução que eu possa chamar no pixel shader. Além disso, essa abordagem exige "nova propagação" dos cabos de texto por vértice, se você deseja valores diferentes a cada quadro, e isso exige que um buffer de vértice atualize cada quadro (o que pode ser caro!)

Com detalhes, como você pode gerar um número aleatório barato na GPU?

bobobobo
fonte
1
O que você quer dizer com por vértice, que tex coord fica interpolado no pixel shader e sua pesquisa por pixel nessa textura aleatória.
Kikaimaru
O que quero dizer é que, se os dois valores de textura estiverem muito próximos, haverá essa dependência (você obterá um valor "combinado"). Digamos que um vértice tenha (0,5,0,5) para u, ve o vértice adjacente tenha (0,51,0,52), e existem 500 fragmentos nesse segmento. As saídas serão 2 ou 3 valores aleatórios reais em textura), mas o resto será interpolado linearmente ao longo da face e não verdadeiramente aleatório. Quero eliminar essa possibilidade e têm uma função rand () que eu posso chamar o pixel shader
bobobobo
Por que você diria que um vértice tem 0,5 e outro 0,51, que não parece aleatório? Você também precisa "randomizar" essas cordas tex, você pode fazê-lo no pixel shader, se quiser, mas são muito mais operações . Esses cabos não precisam ser gerados a partir de sua posição, portanto, não importa o quão próximos eles estejam. Você pode experimentar textura aleatória uma vez no shader de vértice (usando posição, normal, texcoords * Alguns parâmetro como texcoords) e isso vai lhe dar texcoords você passa para pixel shader
Kikaimaru
@Kikaimaru quero dizer com mau acaso, é possível ..
bobobobo
Sim em sequência aleatória sua possível ter dois mesmos valores após o outro
Kikaimaru

Respostas:

14

Números pseudo-aleatórios em um pixel shader não são fáceis de obter. Um gerador de número pseudo-aleatório na CPU terá algum estado no qual ele lê e grava em todas as chamadas para a função. Você não pode fazer isso em um pixel shader.

Aqui estão algumas opções:

  1. Use um sombreador de computação em vez de um sombreador de pixels - eles suportam acesso de leitura e gravação a um buffer, para que você possa implementar qualquer PRNG padrão.

  2. Amostra de uma ou mais texturas contendo dados aleatórios, com base em um parâmetro como a posição do espaço na tela. Você também pode fazer algumas matemáticas na posição antes de usá-la para procurar a textura, o que permitirá reutilizá-la se a matemática envolver uma constante de sombreador de chamada aleatória por sorteio.

  3. Encontre alguma função matemática da posição do espaço na tela, que dê resultados 'aleatórios o suficiente'.

Uma rápida pesquisa no Google encontrou esta página com estas funções:

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;
}
Adão
fonte
Re: computar shaders, você também pode armazenar o estado RNG na memória compartilhada do grupo de discussão. Por questões de eficiência, você não deseja apenas um estado RNG (a disputa seria um gargalo enorme, pois todos os threads tentaram atualizá-lo), mas muitos - talvez até um por thread, ou talvez um por 4 ou 8 threads ou algo parecido. - desde que os estados sejam pequenos o suficiente para tornar isso possível (isto é, provavelmente não Mersenne Twister). Existem projetos RNG nos quais vários encadeamentos SIMD podem cooperar para gerar vários números aleatórios ao mesmo tempo? Isso seria bastante útil aqui.
Nathan Reed
1
o link externo está quebrado
George Birbilis
2

Aqui está como eu gero números pseudo-aleatórios para meus efeitos de partículas com um sombreador de computação. Não tenho certeza de quão verdadeiramente aleatório é esse código, mas funciona suficientemente bem para os meus propósitos.

A chave para dar a cada kernel da GPU sua própria sequência aleatória é alimentar o shader de computação como uma semente aleatória inicial da CPU. Cada kernel usa essa semente para alternar o gerador de números usando seu ID de thread como uma contagem. A partir daí, cada encadeamento deve ter sua própria semente exclusiva para gerar valores únicos.

// 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();
    }
};
KlashnikovKid
fonte