2011-03-01 705 views
9

Estoy tratando de escribir un sombreador muy simple que agrega brillo aleatorio a los objetos aplicables. La forma en que me gustaría hacer esto es agregar un tono aleatorio de blanco (R = G = B) al valor de píxel dentro del sombreador de píxeles.¿Puedo generar un número aleatorio dentro de un sombreador de píxeles?

Parece que noise() no funciona de la manera espero que así sea:

float multiplier = noise(float3(Input.Position[0], Input.Position[1], time)); 

Me da "X4532 error: No se puede asignar a la expresión del conjunto de instrucciones de sombreado de píxeles", en referencia a la llamada a noise().

Como no conozco la forma de retener un número entre las llamadas al sombreador, no creo que pueda simplemente escribir una función de producción de número aleatorio simple basada en una semilla pasada antes de la representación.

¿Hay alguna forma de generar un número aleatorio desde dentro de un sombreador de píxeles? Si hay una forma, ¿cómo?

Respuesta

5

No hay nada que diga que tienes que reutilizar la semilla para que un generador aleatorio se ejecute, solo necesitas una semilla.

Si usa, por ejemplo, la coordenada de píxeles, obtendrá un resultado determinista (es decir, el píxel x, y siempre tendrá el mismo destello aleatorio), pero en general toda la cara se distribuirá aleatoriamente .

Ahora, si tiene disponible como entrada algo que cambia en función del entorno (realmente no sé nada sobre sombreadores de píxeles), como la ubicación general del píxel en el espacio global de la combinación escena/cámara que en relación con el polígono en sí, entonces, especialmente en un entorno de movimiento rápido, sus resultados serán efectivamente aleatorios.

Si todo en el entorno global pasa a ser exactamente el mismo, entonces, sí, tendrá exactamente la misma distribución "aleatoria". Pero si alguno de esos factores cambia (especialmente basado en la entrada del usuario, que probablemente sea la "fuente de ruido" más dinámica) entonces, en general, el efecto será, probablemente, "suficientemente aleatorio".

Por lo tanto, la clave es que su semilla no tiene que ser el resultado de una ejecución anterior del generador de números aleatorios. Puede ser cualquier cosa. Por lo tanto, idear una semilla para cada píxel en función de las entradas del sombreador a su propio RNG puede darle el efecto que necesita.

+0

Aunque ambas respuestas son útiles, esta se ajusta mejor a lo que estaba buscando. ¡Gracias! – chaosTechnician

8

Lo que generalmente hace cuando quiere un valor aleatorio en un sombreador de píxeles es pasar una textura que contiene ruido. Si bien no es realmente "aleatorio", es se ve aleatorio.

Por ejemplo, aquí hay un código de un pixel shader He ahí:

float3 random = (tex2D(noiseTexture, texCoord * noiseScale + noiseOffset)); 

La textura que utilizo es una textura RGB-ruido, que puede ser útil a veces. Pero la misma técnica funcionaría para una escala de grises.

Escalando que me aseguro de que los píxeles de la línea de la textura de ruido hasta los pixeles de la pantalla (es posible que también desee establecer el muestreador textura a modo de "punto" por lo que no falta de definición de la textura de ruido).

Al usar un desplazamiento, puede desplazar la textura, que es como sembrar un generador de números aleatorios. Use un desplazamiento al azar si desea evitar ese aspecto de "desplazamiento".

24

actualización de julio de 2017 He hecho el "pseudo-aleatoriedad" más estable

// Version 3 
float random(vec2 p) 
{ 
    vec2 K1 = vec2(
     23.14069263277926, // e^pi (Gelfond's constant) 
     2.665144142690225 // 2^sqrt(2) (Gelfond–Schneider constant) 
    ); 
    return fract(cos(dot(p,K1)) * 12345.6789); 
} 

Esta son las versiones:

float random(vec2 p) 
{ 
    // e^pi (Gelfond's constant) 
    // 2^sqrt(2) (Gelfond–Schneider constant) 
    vec2 K1 = vec2(23.14069263277926, 2.665144142690225); 

    //return fract(cos(mod(12345678., 256. * dot(p,K1)))); // ver1 
    //return fract(cos(dot(p,K1)) * 123456.); // ver2 
    return fract(cos(dot(p,K1)) * 12345.6789); // ver3 
} 

// Minified version 3: 
float random(vec2 p){return fract(cos(dot(p,vec2(23.14069263277926,2.665144142690225)))*12345.6789);} 

aéreas en una textura a generar ruido está (generalmente) sobre ingeniería. Hay momentos en los que es útil, pero para la mayoría de los casos es más simple y más rápido calcular un número aleatorio.

Dado que las variables de sombreado son independientes por fragmento, no pueden reutilizar las variables existentes entre ellas. El problema entonces se convierte en uno de cómo usar una semilla "buena" de números aleatorios. Los números irracionales parecen encajar en la factura para empezar. Entonces es solo una cuestión 'simple' de elegir una buena función de "permutar".

Aquí hay un código libre que hace el truco:

// Input: It uses texture coords as the random number seed. 
// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive. 
// Author: Michael Pohoreski 
// Copyright: Copyleft 2012 :-) 
// NOTE: This has been upgraded to version 3 !! 
float random(vec2 p) 
{ 
    // We need irrationals for pseudo randomness. 
    // Most (all?) known transcendental numbers will (generally) work. 
    const vec2 r = vec2(
    23.1406926327792690, // e^pi (Gelfond's constant) 
    2.6651441426902251); // 2^sqrt(2) (Gelfond–Schneider constant) 
    return fract(cos(mod(123456789., 1e-7 + 256. * dot(p,r)))); 
} 

Para entender cómo funciona esto si rompemos la fórmula hacia abajo en sus partes constituyentes se hace más fácil de visualizar lo que está pasando:

const vec2 k = vec2(23.1406926327792690,2.6651441426902251); 
float rnd0(vec2 uv) {return dot(uv,k); } 
float rnd1(vec2 uv) { return 1e-7 + 256. + dot(uv,k); } 
float rnd2(vec2 uv) { return mod(123456789., 256. * dot(uv,k)); } 
float rnd3(vec2 uv) { return cos(mod(123456789., 256. * dot(uv,k))); } 

// We can even tweak the formula 
float rnd4(vec2 uv) { return fract(cos(mod(1234., 1024. * dot(uv,k)))); } 
float rnd5(vec2 uv) { return fract(cos(mod(12345., 1024. * dot(uv,k)))); } 
float rnd6(vec2 uv) { return fract(cos(mod(123456., 1024. * dot(uv,k)))); } 
float rnd7(vec2 uv) { return fract(cos(mod(1234567., 1024. * dot(uv,k)))); } 
float rnd8(vec2 uv) { return fract(cos(mod(12345678., 1024. * dot(uv,k)))); } 
float rnd9(vec2 uv) { return fract(cos(mod(123456780., 1024. * dot(uv,k)))); } 

void mainImage(out vec4 fragColor, in vec2 fragCoord) 
{ 
    mediump vec2 uv = fragCoord.xy/iResolution.xy; 
    float i = rnd9(uv); 
    fragColor = vec4(i,i,i,1.); 
} 

Pegado lo anterior en:

También he creado una "comparación" ejemplo ShaderToy con 2 funciones de ruido, y 2 funciones aleatorias:

demostración de la utilización de ruido "[2TC 15] moteado Cruz fade"

una función aleatoria "clásico", a veces llamado snoise3 es este bad one:

return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); 

Si desea comparar "pseudo aleatorios" funciones echa un vistazo a Hash without sine shader de Dave.

+0

Nice: D ¡Gracias! – Raptormeat

+0

Interesante. Aunque no estoy de acuerdo con que el método de textura sea demasiado ingenioso. Está intercambiando una búsqueda de textura por un número no despreciable de instrucciones. Y aún tiene que pasar un desplazamiento al azar en cada cuadro. Yo diría que ambos métodos son válidos. –

+0

@AndrewRussell Con las GPU modernas, es (generalmente) mucho más rápido hacer un cálculo (vinculado a la CPU) luego la búsqueda de textura (IO enlazado). Si necesita un número "aleatorio" por píxel, es trivial pasar las coordenadas de la textura. Pongo la palabra entre paréntesis porque la única forma de * saber * para * su * plataforma es medir el tiempo de ambos. ;-) – Michaelangel007

Cuestiones relacionadas