worley - glsl random
Funciones aleatorias/de ruido para GLSL (8)
Dado que los proveedores de controladores de GPU generalmente no se molestan en implementar noiseX
en GLSL, estoy buscando un conjunto de funciones de utilidad de "aleatorización de gráficos aleatorias" , preferiblemente optimizado para usar dentro de los sombreadores de GPU. Prefiero GLSL, pero el código de cualquier idioma me servirá, estoy de acuerdo con traducirlo por mi cuenta a GLSL.
Específicamente, esperaría:
a) Funciones pseudoaleatorias : distribución N-dimensional, uniforme sobre [-1,1] o superior a [0,1], calculada a partir de una semilla de dimensión M (lo ideal sería cualquier valor, pero estoy de acuerdo con que se refrene la semilla a, digamos, 0..1 para distribución de resultados uniforme). Algo como:
float random (T seed);
vec2 random2 (T seed);
vec3 random3 (T seed);
vec4 random4 (T seed);
// T being either float, vec2, vec3, vec4 - ideally.
b) Ruido continuo como Perlin Noise - de nuevo, N-dimensional, + - distribución uniforme, con un conjunto limitado de valores y, bueno, luciendo bien (algunas opciones para configurar el aspecto como los niveles de Perlin también podrían ser útiles). Esperaría firmas como:
float noise (T coord, TT seed);
vec2 noise2 (T coord, TT seed);
// ...
No estoy muy interesado en la teoría de generación de números aleatorios, así que preferiría una solución prefabricada , pero también agradecería las respuestas como "aquí hay un 1D rand muy bueno y eficiente (), y déjame explicarte cómo hacer un buen rand N-dimensional () encima ... " .
La implementación de Gustavson usa una textura 1D
No, no, no desde 2005. Es solo que la gente insiste en descargar la versión anterior. La versión que está en el enlace que proporcionó utiliza solo texturas 2D de 8 bits.
La nueva versión de Ian McEwan de Ashima y de mí mismo no usa una textura, pero funciona a la mitad de la velocidad en plataformas de escritorio típicas con mucho ancho de banda de textura. En las plataformas móviles, la versión sin texturas puede ser más rápida porque texturizar suele ser un cuello de botella importante.
Nuestro repositorio fuente mantenido activamente es:
Aquí está disponible una colección de versiones de ruido sin texturas y que usan texturas (usando solo texturas 2D):
http://www.itn.liu.se/~stegu/simplexnoise/GLSL-noise-vs-noise.zip
Si tiene alguna pregunta específica, no dude en enviarme un correo electrónico directamente (mi dirección de correo electrónico se puede encontrar en las fuentes classicnoise*.glsl
).
Acabo de encontrar esta versión de ruido 3D para GPU, sin dudas es la más rápida disponible:
#ifndef __noise_hlsl_
#define __noise_hlsl_
// hash based 3d value noise
// function taken from https://www.shadertoy.com/view/XslGRr
// Created by inigo quilez - iq/2013
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// ported from GLSL to HLSL
float hash( float n )
{
return frac(sin(n)*43758.5453);
}
float noise( float3 x )
{
// The noise function returns a value in the range -1.0f -> 1.0f
float3 p = floor(x);
float3 f = frac(x);
f = f*f*(3.0-2.0*f);
float n = p.x + p.y*57.0 + 113.0*p.z;
return lerp(lerp(lerp( hash(n+0.0), hash(n+1.0),f.x),
lerp( hash(n+57.0), hash(n+58.0),f.x),f.y),
lerp(lerp( hash(n+113.0), hash(n+114.0),f.x),
lerp( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
}
#endif
Para cosas de aspecto pseudoaleatorio muy simples, uso este delineador que encontré en Internet en alguna parte:
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
También puede generar una textura de ruido usando cualquier PRNG que desee, luego súbalo de la manera normal y muestree los valores en su sombreador; Puedo desenterrar un ejemplo de código más tarde si lo desea.
Además, echa un vistazo a este archivo para las implementaciones GLSL de ruido Perlin y Simplex, por Stefan Gustavson.
Se me ocurre que puede usar una función hash entera simple e insertar el resultado en la mantisa de un flotador. IIRC la especificación GLSL garantiza enteros sin signo de 32 bits y representación flotante IEEE binary32, por lo que debe ser perfectamente portátil.
Hice una prueba ahora. Los resultados son muy buenos: se ve exactamente igual a estático con cada entrada que intenté, sin patrones visibles en absoluto. En contraste, el popular fragmento de pecado / fract tiene líneas diagonales bastante pronunciadas en mi GPU con las mismas entradas.
Una desventaja es que requiere GLSL v3.30. Y aunque parece lo suficientemente rápido, no he cuantificado empíricamente su rendimiento. El Shader Analyzer de AMD reclama 13.33 píxeles por reloj para la versión vec2 en un HD5870. Contraste con 16 píxeles por reloj para el fragmento sin / fract. Por lo tanto, es un poco más lento.
Aquí está mi implementación. Lo dejé en varias permutaciones de la idea para que sea más fácil derivar tus propias funciones.
/*
static.frag
by Spatial
05 July 2013
*/
#version 330 core
uniform float time;
out vec4 fragment;
// A single iteration of Bob Jenkins'' One-At-A-Time hashing algorithm.
uint hash( uint x ) {
x += ( x << 10u );
x ^= ( x >> 6u );
x += ( x << 3u );
x ^= ( x >> 11u );
x += ( x << 15u );
return x;
}
// Compound versions of the hashing algorithm I whipped together.
uint hash( uvec2 v ) { return hash( v.x ^ hash(v.y) ); }
uint hash( uvec3 v ) { return hash( v.x ^ hash(v.y) ^ hash(v.z) ); }
uint hash( uvec4 v ) { return hash( v.x ^ hash(v.y) ^ hash(v.z) ^ hash(v.w) ); }
// Construct a float with half-open range [0:1] using low 23 bits.
// All zeroes yields 0.0, all ones yields the next smallest representable value below 1.0.
float floatConstruct( uint m ) {
const uint ieeeMantissa = 0x007FFFFFu; // binary32 mantissa bitmask
const uint ieeeOne = 0x3F800000u; // 1.0 in IEEE binary32
m &= ieeeMantissa; // Keep only mantissa bits (fractional part)
m |= ieeeOne; // Add fractional part to 1.0
float f = uintBitsToFloat( m ); // Range [1:2]
return f - 1.0; // Range [0:1]
}
// Pseudo-random value in half-open range [0:1].
float random( float x ) { return floatConstruct(hash(floatBitsToUint(x))); }
float random( vec2 v ) { return floatConstruct(hash(floatBitsToUint(v))); }
float random( vec3 v ) { return floatConstruct(hash(floatBitsToUint(v))); }
float random( vec4 v ) { return floatConstruct(hash(floatBitsToUint(v))); }
void main()
{
vec3 inputs = vec3( gl_FragCoord.xy, time ); // Spatial and temporal inputs
float rand = random( inputs ); // Random per-pixel value
vec3 luma = vec3( rand ); // Expand to RGB
fragment = vec4( luma, 1.0 );
}
Captura de pantalla:
Inspeccioné la captura de pantalla en un programa de edición de imágenes. Hay 256 colores y el valor promedio es 127, lo que significa que la distribución es uniforme y cubre el rango esperado.
También hay una buena implementación descrita here por McEwan y @StefanGustavson que se parece al ruido de Perlin, pero "no requiere ninguna configuración, es decir, ni texturas ni matrices uniformes. Simplemente agrégala al código fuente de tu sombreador y llámalo a donde quieras".
Eso es muy útil, especialmente teniendo en cuenta que la implementación anterior de Gustavson, a la que @dep se vincula, utiliza una textura 1D, que no es compatible con GLSL ES (el lenguaje de sombreado de WebGL).
Una versión recta y dentada de 1d Perlin, esencialmente un zfozag lfo aleatorio.
half rn(float xx){
half x0=floor(xx);
half x1=x0+1;
half v0 = frac(sin (x0*.014686)*31718.927+x0);
half v1 = frac(sin (x1*.014686)*31718.927+x1);
return (v0*(1-frac(xx))+v1*(frac(xx)))*2-1*sin(xx);
}
Vea a continuación un ejemplo de cómo agregar ruido blanco a la textura procesada. La solución es usar dos texturas: ruido blanco puro y original, como este: ruido blanco wiki
private static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix;/n" +
"uniform mat4 uMVMatrix;/n" +
"uniform mat4 uSTMatrix;/n" +
"attribute vec4 aPosition;/n" +
"attribute vec4 aTextureCoord;/n" +
"varying vec2 vTextureCoord;/n" +
"varying vec4 vInCamPosition;/n" +
"void main() {/n" +
" vTextureCoord = (uSTMatrix * aTextureCoord).xy;/n" +
" gl_Position = uMVPMatrix * aPosition;/n" +
"}/n";
private static final String FRAGMENT_SHADER =
"precision mediump float;/n" +
"uniform sampler2D sTextureUnit;/n" +
"uniform sampler2D sNoiseTextureUnit;/n" +
"uniform float uNoseFactor;/n" +
"varying vec2 vTextureCoord;/n" +
"varying vec4 vInCamPosition;/n" +
"void main() {/n" +
" gl_FragColor = texture2D(sTextureUnit, vTextureCoord);/n" +
" vec4 vRandChosenColor = texture2D(sNoiseTextureUnit, fract(vTextureCoord + uNoseFactor));/n" +
" gl_FragColor.r += (0.05 * vRandChosenColor.r);/n" +
" gl_FragColor.g += (0.05 * vRandChosenColor.g);/n" +
" gl_FragColor.b += (0.05 * vRandChosenColor.b);/n" +
"}/n";
El fragmento compartido contiene el parámetro uNoiseFactor que se actualiza en cada representación por aplicación principal:
float noiseValue = (float)(mRand.nextInt() % 1000)/1000;
int noiseFactorUniformHandle = GLES20.glGetUniformLocation( mProgram, "sNoiseTextureUnit");
GLES20.glUniform1f(noiseFactorUniformHandle, noiseFactor);
Ruido de oro
// Gold Noise ©2017-2018 [email protected]
// - based on the Golden Ratio, PI and Square Root of Two
// - fastest noise generator function
// - works with all chipsets (including low precision)
precision lowp float;
float PHI = 1.61803398874989484820459 * 00000.1; // Golden Ratio
float PI = 3.14159265358979323846264 * 00000.1; // PI
float SQ2 = 1.41421356237309504880169 * 10000.0; // Square Root of Two
float gold_noise(in vec2 coordinate, in float seed){
return fract(sin(dot(coordinate*(seed+PHI), vec2(PHI, PI)))*SQ2);
}
¡Vea Gold Noise en su navegador ahora mismo!
Esta función ha mejorado la distribución aleatoria sobre la función actual en la respuesta de @appas a partir del 9 de septiembre de 2017:
La función @appas también está incompleta, dado que no se suministra semilla (uv no es una semilla, lo mismo para cada cuadro), y no funciona con chipsets de baja precisión. Gold Noise funciona con baja precisión por defecto (mucho más rápido).