Número aleatorio hlsl

13

¿Cómo se genera un número aleatorio en HLSL?

Lo pregunto porque quiero probar el trazado de rayos gpu . Necesita generar direcciones aleatorias en un sombreador de píxeles. Entonces, quiero randFloat(), donde el resultado es un número aleatorio entre -1 y +1.

Además, ¿cuál es el trato con la instrucción de ruido hlsl ? Los documentos dicen que se eliminó de HLSL 2.0 y versiones posteriores. ¿Por qué ?

Leí sobre un enfoque en el que rellena una textura con valores aleatorios, luego tiene una coordenada de textura en cada vértice que tiene un índice en esa textura. Pero eso es por vértice , necesito una instrucción que pueda llamar en el sombreador de píxeles. Además, este enfoque requiere "volver a colocar" los códigos de texto por vértice si desea valores diferentes en cada cuadro, y eso requiere una actualización del búfer de vértices en cada cuadro (¡lo cual puede ser costoso!)

Con detalles, ¿cómo puede generar de forma económica un número aleatorio en la GPU?

bobobobo
fuente
1
¿Qué quiere decir con por vértice, que tex coord se interpola en el sombreador de píxeles y su búsqueda por píxel a esa textura aleatoria.
Kikaimaru
Lo que quiero decir es que, si los 2 valores de textura están demasiado juntos, habrá esta dependencia (obtendrá un valor "combinado"). Digamos que un vértice tiene (0.5,0.5) para u, v y el vértice adyacente tiene (0.51,0.52), y hay como 500 fragmentos en ese segmento ... entonces las salidas serán 2 o 3 valores aleatorios reales (consultado textura), pero el resto se interpolará linealmente a lo largo de la cara y no realmente al azar. Quiero eliminar esa posibilidad y tener una función rand () a la que puedo llamar en el sombreador de píxeles
bobobobo
¿Por qué diría que un vértice tiene 0.5 y otro 0.51, que no suena al azar en absoluto, necesita "aleatorizar" también estas coordenadas de texto, puede hacerlo en pixel shader si lo desea, pero eso es muchas más operaciones . Esos códigos de texto no necesitan ser generados desde su posición, por lo que no importa cuán cerca estén uno del otro. Puede muestrear texturas aleatorias una vez en el sombreador de vértices (usando position, normal, texcoords * algún parámetro como texcoords) y esto le dará los texcoords que pase al sombreador de píxeles
Kikaimaru
@Kikaimaru quiero decir con mala casualidad, es posible ...
bobobobo
Sí, en secuencia aleatoria, es posible tener dos valores iguales uno detrás del otro
Kikaimaru

Respuestas:

14

Los números pseudoaleatorios en un sombreador de píxeles no son fáciles de obtener. Un generador de números pseudoaleatorios en la CPU tendrá un estado en el que lee y escribe, en cada llamada a la función. No puedes hacer eso en un sombreador de píxeles.

Aquí hay algunas opciones:

  1. Use un sombreador de cómputo en lugar de un sombreador de píxeles: admiten el acceso de lectura y escritura a un búfer, por lo que puede implementar cualquier PRNG estándar.

  2. Muestra de una o más texturas que contienen datos aleatorios, en función de un parámetro como la posición del espacio de pantalla. También puede hacer algunos cálculos matemáticos en la posición antes de usarlos para mirar hacia arriba en la textura, lo que debería permitirle reutilizar la textura si las matemáticas implican una constante de sombreador aleatorio por llamada de sorteo.

  3. Encuentre alguna función matemática de la posición del espacio de la pantalla, que dé resultados que sean 'suficientemente aleatorios'.

Una búsqueda rápida en Google encontró esta página con estas funciones:

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án
fuente
Re: calcular sombreadores, también podría almacenar el estado RNG en la memoria compartida del grupo de subprocesos. Para mayor eficiencia, no desea solo un estado RNG (la contención sería un gran cuello de botella ya que cada hilo intentaba actualizarlo), sino muchos, tal vez hasta uno por hilo, o tal vez uno por 4 u 8 hilos o algo así - siempre y cuando los estados sean lo suficientemente pequeños como para hacer que sea factible (es decir, probablemente no Mersenne Twister). ¿Existen diseños RNG en los que varios hilos SIMD pueden cooperar para generar varios números aleatorios a la vez? Eso sería bastante útil aquí.
Nathan Reed
1
el enlace externo está roto
George Birbilis
2

Así es como genero números pseudoaleatorios para mis efectos de partículas con un sombreador de cálculo. No estoy seguro de cuán verdaderamente aleatorio es este código, pero funciona lo suficientemente bien para mis propósitos.

La clave para darle a cada núcleo de GPU su propia secuencia aleatoria es alimentar al sombreador de cómputo con una semilla inicial aleatoria de la CPU. Cada kernel luego usa esta semilla para hacer un ciclo del generador de números usando su id de hilo como recuento. A partir de ahí, cada hilo debe tener su propia semilla única para generar 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
fuente