funciones aleatoria / ruido para GLSL
-
25-09-2019 - |
Pregunta
A medida que la GPU proveedores de controladores por lo general no se molestan en poner en práctica noiseX
en GLSL, estoy buscando un "gráficos aleatorización navaja suiza" conjunto de funciones de utilidad, preferentemente optimizado para su uso dentro de los sombreadores de GPU . Yo prefiero GLSL, pero el código cualquier idioma va a hacer por mí, estoy bien con la traducción por mi cuenta a GLSL.
En concreto, esperaría:
a) funciones pseudo-aleatoria - N-dimensional, la distribución uniforme en [-1,1] o sobre [0,1], calculado a partir de semillas M-dimensional (siendo idealmente cualquier valor, pero yo estoy bien con tener la semilla restringido a, por ejemplo, 0..1 para la distribución uniforme de resultados). Algo así 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 restringido de los valores y, así, en buen estado (algunas opciones para configurar la apariencia de los niveles de Perlin podría ser útil también). Yo esperaría firmas como:
float noise (T coord, TT seed);
vec2 noise2 (T coord, TT seed);
// ...
No estoy mucho en la teoría de generación de números aleatorios, por lo que había más ansiosamente a dar un solución de pre-hechos , pero yo también aprecio respuestas como ", una de aquí muy bueno, rand 1D eficiente (), y deja que te explique cómo hacer un buen N-dimensional rand () en la parte superior de la misma ... ".
Solución
Por muy simple cosas pseudo-mirando, yo uso esta oneliner que he encontrado en algún lugar de la Internet:
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 utilizando cualquier PRNG se quiere, a continuación, cargar esto en la manera normal y muestra los valores en su shader; Puedo desenterrar un ejemplo de código más tarde si lo desea.
Además, echa un vistazo a este archivo para implementaciones GLSL de Perlin y Simplex ruido , de Stefan Gustavson.
Otros consejos
aplicación de Gustavson utiliza una textura 1D
No, no, no desde el año 2005. Es que la gente insiste en la descarga de la versión antigua. La versión que se encuentra en el enlace que suministra usos única de 8 bits en 2D texturas.
La nueva versión de Ian McEwan de Ashima y yo no utiliza una textura, pero se ejecuta en alrededor de la mitad de la velocidad en las plataformas de escritorio típico con una gran cantidad de ancho de banda textura. En las plataformas móviles, la versión sin textura podría ser más rápido porque texturizado es a menudo un importante cuello de botella.
Nuestro repositorio de código fuente mantenido activa es:
https://github.com/ashima/webgl-noise
Una colección tanto de la textureless y versiones textura que utilizan de ruido está aquí (usando sólo 2D texturas):
http://www.itn.liu.se/~stegu /simplexnoise/GLSL-noise-vs-noise.zip
Si usted 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
.)
Se me ocurre que se puede utilizar una función de hash entero sencilla e insertar el resultado en mantisa de un flotador. IIRC garantiza la GLSL especificación de 32 bits enteros sin signo y representación flotante IEEE binary32 lo que debe ser perfectamente portátil.
Me dio a este un intento en este momento. Los resultados son muy buenos: se ve exactamente como estático con cada entrada he intentado, no hay patrones visibles en absoluto. En contraste, el pecado populares / fragmento bastante fract ha pronunciado líneas diagonales en mi GPU dadas las mismas entradas.
Una desventaja es que requiere v3.30 GLSL. Y aunque parezca lo suficientemente rápido, no he cuantificado empíricamente su rendimiento. Shader Analizador de AMD afirma 13.33 píxeles por ciclo de reloj para la versión vec2 en una HD5870. Contraste con 16 píxeles por reloj para el sen / fragmento fract. Así que sin duda es un poco más lento.
Aquí está mi aplicación. Lo dejé en varias permutaciones de la idea de hacer que sea más fácil para derivar sus propias funciones de.
/*
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:
Me inspeccionó la captura de pantalla en un programa de edición de imágenes. Hay 256 colores y el valor promedio es de 127, lo que significa la distribución es uniforme y cubre el rango esperado.
Oro ruido
// Gold Noise ©2015 dcerisano@standard3d.com
// - based on the Golden Ratio, PI and Square Root of Two
// - superior distribution
// - fastest noise generator function
// - works with all chipsets (including low precision)
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(tan(distance(coordinate*(seed+PHI), vec2(PHI, PI)))*SQ2);
}
Ver Oro de ruido en su navegador en este momento!
Esta función tiene una mejor distribución aleatoria sobre la función actual en respuesta @appas' como de 09 de septiembre 2017:
La función @appas también es incompleta, dado que no hay semillas suministrado (UV no es una semilla - el mismo para cada trama), y no funciona con conjuntos de chips de baja precisión. carreras de ruido de oro a la baja precisión por defecto (mucho más rápido).
También hay una buena implementación descrita aquí por McEwan y @StefanGustavson que se parece a Perlin ruido, pero "no requiere ningún tipo de configuración, es decir, no texturas ni matrices uniformes. Sólo tiene que añadir a su código fuente shader y llamar siempre que lo desee".
Esto es muy útil, especialmente teniendo en cuenta que la aplicación anterior de Gustavson, que @dep vinculada a, usa una textura 1D, que es no admitido en GLSL ES (el lenguaje de sombreado de WebGL).
Sólo encontraron esta versión de ruido 3D para la GPU, alledgedly es el más rápido 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
A recta, versión dentado de 1d Perlin, esencialmente un zigzag 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);
}
También he encontrado ruido Perlin 1-2-3-4d el propietario shadertoy inigo quilez perlin tutorial sitio web, y de Voronoi y así sucesivamente, tiene implementaciones rápidas completos y códigos para ellos.
de hash: Hoy en día hay tan webGL2.0 enteros están disponibles en GLSL (w). -> Hash portátil de calidad (a un costo similar al de los hashes de flotación feos) que ahora podemos utilizar técnicas de "graves" hash. IQ implementado algunos en https://www.shadertoy.com/view/XlXcW4 (y más )
por ejemplo:.
const uint k = 1103515245U; // GLIB C
//const uint k = 134775813U; // Delphi and Turbo Pascal
//const uint k = 20170906U; // Today's date (use three days ago's dateif you want a prime)
//const uint k = 1664525U; // Numerical Recipes
vec3 hash( uvec3 x )
{
x = ((x>>8U)^x.yzx)*k;
x = ((x>>8U)^x.yzx)*k;
x = ((x>>8U)^x.yzx)*k;
return vec3(x)*(1.0/float(0xffffffffU));
}
que usan esta corriente:
highp float rand(vec2 co)
{
highp float a = 12.9898;
highp float b = 78.233;
highp float c = 43758.5453;
highp float dt= dot(co.xy ,vec2(a,b));
highp float sn= mod(dt,3.14);
return fract(sin(sn) * c);
}
No utilice esto:
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
Se puede encontrar la explicación en Mejoras a la canónica de una sola línea rand GLSL () para OpenGL ES 2.0
Por favor, ver más abajo un ejemplo de cómo añadir ruido blanco para la textura prestado. La solución es utilizar dos texturas: ruido blanco original y pura, como éste: wiki ruido blanco
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 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);
He traducido una de las implementaciones de Java de Ken Perlin en GLSL y lo usó en un par de proyectos en ShaderToy.
A continuación se muestra la interpretación GLSL que hice:
int b(int N, int B) { return N>>B & 1; }
int T[] = int[](0x15,0x38,0x32,0x2c,0x0d,0x13,0x07,0x2a);
int A[] = int[](0,0,0);
int b(int i, int j, int k, int B) { return T[b(i,B)<<2 | b(j,B)<<1 | b(k,B)]; }
int shuffle(int i, int j, int k) {
return b(i,j,k,0) + b(j,k,i,1) + b(k,i,j,2) + b(i,j,k,3) +
b(j,k,i,4) + b(k,i,j,5) + b(i,j,k,6) + b(j,k,i,7) ;
}
float K(int a, vec3 uvw, vec3 ijk)
{
float s = float(A[0]+A[1]+A[2])/6.0;
float x = uvw.x - float(A[0]) + s,
y = uvw.y - float(A[1]) + s,
z = uvw.z - float(A[2]) + s,
t = 0.6 - x * x - y * y - z * z;
int h = shuffle(int(ijk.x) + A[0], int(ijk.y) + A[1], int(ijk.z) + A[2]);
A[a]++;
if (t < 0.0)
return 0.0;
int b5 = h>>5 & 1, b4 = h>>4 & 1, b3 = h>>3 & 1, b2= h>>2 & 1, b = h & 3;
float p = b==1?x:b==2?y:z, q = b==1?y:b==2?z:x, r = b==1?z:b==2?x:y;
p = (b5==b3 ? -p : p); q = (b5==b4 ? -q : q); r = (b5!=(b4^b3) ? -r : r);
t *= t;
return 8.0 * t * t * (p + (b==0 ? q+r : b2==0 ? q : r));
}
float noise(float x, float y, float z)
{
float s = (x + y + z) / 3.0;
vec3 ijk = vec3(int(floor(x+s)), int(floor(y+s)), int(floor(z+s)));
s = float(ijk.x + ijk.y + ijk.z) / 6.0;
vec3 uvw = vec3(x - float(ijk.x) + s, y - float(ijk.y) + s, z - float(ijk.z) + s);
A[0] = A[1] = A[2] = 0;
int hi = uvw.x >= uvw.z ? uvw.x >= uvw.y ? 0 : 1 : uvw.y >= uvw.z ? 1 : 2;
int lo = uvw.x < uvw.z ? uvw.x < uvw.y ? 0 : 1 : uvw.y < uvw.z ? 1 : 2;
return K(hi, uvw, ijk) + K(3 - hi - lo, uvw, ijk) + K(lo, uvw, ijk) + K(0, uvw, ijk);
}
Lo traduje del Apéndice B del Capítulo 2 de hardware ruido de Ken Perlin en esta fuente:
https://www.csee.umbc.edu/~olano /s2002c36/ch02.pdf
Aquí es un tono pública que hice en Shader juguete que utiliza la función de ruido de publicación:
https://www.shadertoy.com/view/3slXzM
Algunas otras fuentes buenas que he encontrado sobre el tema del ruido durante mi investigación incluyen:
https://thebookofshaders.com/11/
https://mzucker.github.io/html/perlin -ruido-matemáticas-faq.html
https://rmarcus.info/blog/2018/03 /04/perlin-noise.html
http://flafla2.github.io/2014/08/09 /perlinnoise.html
https://mrl.nyu.edu/~perlin/noise/
https://rmarcus.info/blog/assets/perlin/perlin_paper.pdf
https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch05.html
le recomiendo el libro de shaders ya que no sólo ofrece una gran explicación interactiva de ruido, pero otros conceptos de sombreado también.
EDIT:
podría ser capaz de optimizar el código traducido mediante el uso de algunas de las funciones de aceleración de hardware disponibles en GLSL. Se actualizará este post si termino haciendo esto.