web-dev-qa-db-fra.com

Fonctions aléatoires/bruit pour GLSL

Les éditeurs de pilotes GPU ne prenant généralement pas la peine d’implémenter noiseX en GLSL, je recherche un ensemble de fonctions d’utilité "graphisme suisse couteau militaire", optimisé de préférence pour être utilisé dans les shaders GPU. Je préfère le GLSL, mais le code de n'importe quelle langue me convient, je peux le traduire moi-même en GLSL.

Plus précisément, je m'attendrais à:

a) Fonctions pseudo-aléatoires _ - Distribution uniforme à N dimensions sur [-1,1] ou sur [0,1], calculée à partir de la graine de dimension M (idéalement, n'importe quelle valeur, mais OK avec le maintien de la graine sur, disons, 0..1 pour une distribution de résultat uniforme). Quelque chose comme:

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) Bruit continu comme Perlin Noise - encore une fois, distribution N-dimensionnelle, + - uniforme, avec jeu de valeurs contraint et, bien, bonne apparence (certaines options de configuration de l'apparence comme les niveaux de Perlin pourraient également être utiles ). J'attendrais des signatures comme:

float noise  (T coord, TT seed);
vec2  noise2 (T coord, TT seed);
// ...

Je ne suis pas très habitué à la théorie de la génération de nombres aléatoires, je préférerais donc vivement une solution préconfigurée, mais j'apprécierais également des réponses telles que "voici un très bon et efficace 1D Rand (), et laissez-moi vous expliquer comment faire un bon Rand () N-dimensionnel par-dessus ... ".

151
Kos

Pour des choses très simples d'aspect pseudo-aléatoire, j'utilise cet oneliner que j'ai trouvé quelque part sur Internet:

float Rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

Vous pouvez également générer une texture de bruit en utilisant le PRNG de votre choix, puis l'importer de la manière habituelle et échantillonner les valeurs dans votre shader; Je peux creuser un échantillon de code plus tard si vous le souhaitez.

Consultez également ce fichier pour les implémentations GLSL de bruit de Perlin et de Simplex, par Stefan Gustavson. 

241
appas

L'implémentation de Gustavson utilise une texture 1D

Non, pas depuis 2005. C'est juste que les gens insistent pour télécharger l'ancienne version. La version figurant sur le lien que vous avez fourni utilise uniquement des textures 2D 8 bits.

Ian McEwan, de Ashima, et moi-même n'utilisons pas de texture, mais une vitesse deux fois moins rapide que celle des plates-formes de bureau classiques offrant une grande largeur de bande passante. Sur les plates-formes mobiles, la version sans texture pourrait être plus rapide car la texturation est souvent un goulot d'étranglement important.

Notre référentiel source maintenu activement est:

https://github.com/ashima/webgl-noise

Une collection des versions de bruit sans texture et utilisant une texture est ici (en utilisant uniquement des textures 2D):

http://www.itn.liu.se/~stegu/simplexnoise/GLSL-noise-vs-noise.Zip

Si vous avez des questions spécifiques, n'hésitez pas à m'envoyer un mail directement (mon adresse email se trouve dans les sources classicnoise*.glsl.)

69
Stefan Gustavson

Il me semble que vous pourriez utiliser une simple fonction de hachage entier et insérer le résultat dans la mantisse d'un float. Le standard IIRC GLSL garantit des entiers non signés sur 32 bits et une représentation float binaire32 IEEE. Il doit donc être parfaitement portable.

Je l'ai essayé tout à l'heure. Les résultats sont très bons: cela ressemble exactement à statique avec chaque entrée que j'ai essayée, aucun motif visible du tout. En revanche, le fragment populaire péché/fracture présente des lignes diagonales assez prononcées sur mon GPU avec les mêmes entrées.

Un inconvénient est que cela nécessite GLSL v3.30. Et bien que cela semble assez rapide, je n'ai pas chiffré ses performances de manière empirique. Shader Analyzer d'AMD revendique 13,33 pixels par horloge pour la version vec2 sur un HD5870. Contraste avec 16 pixels par horloge pour l'extrait péché/fracture. Donc c'est certainement un peu plus lent.

Voici ma mise en œuvre. Je l'ai laissée dans différentes permutations de l'idée pour faciliter la déduction de vos propres fonctions.

/*
    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 );
}

Capture d'écran:

Output of random(vec3) in static.frag

J'ai inspecté la capture d'écran dans un programme d'édition d'image. Il y a 256 couleurs et la valeur moyenne est 127, ce qui signifie que la distribution est uniforme et couvre la plage attendue.

63
Spatial

Bruit d'or

// Gold Noise ©2015 [email protected] 
//  - 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);
}

Voir Gold Noise dans votre navigateur dès maintenant!

 enter image description here

Cette fonction a amélioré la distribution aléatoire par rapport à la fonction actuelle dans la réponse de @appas à compter du 9 septembre 2017:

 enter image description here

La fonction @appas est également incomplète, étant donné qu’aucune graine n’est fournie (UV n’est pas une graine - la même pour chaque image) et ne fonctionne pas avec les chipsets de faible précision. Le bruit doré fonctionne par défaut avec une faible précision (beaucoup plus rapide).

18
Dominic Cerisano

Il existe également une implémentation de Nice décrite ici par McEwan et @StefanGustavson qui ressemble au bruit Perlin, mais "ne nécessite aucune configuration, c'est-à-dire ni textures ni tableaux uniformes. Ajoutez-le simplement à votre code source de shader tu veux".

C'est très pratique, d'autant plus que la mise en œuvre antérieure de Gustavson, à laquelle @dep est liée, utilise une texture 1D, qui est non prise en charge dans GLSL ES (le langage shader de WebGL).

11
LarsH

Je viens de trouver cette version du bruit 3D pour GPU.

#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
2
com.prehensible

Une version rectiligne et déchiquetée de 1d Perlin, essentiellement un zig-zag lfo aléatoire. 

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);
}

J'ai également trouvé le bruit 1-2-3-4d Perlin sur le site Web du didacticiel inigo quilez perlin du propriétaire de shadertoy, et voronoi, etc., il dispose de nombreuses implémentations et codes rapides. 

1
com.prehensible

Vous trouverez ci-dessous un exemple expliquant comment ajouter du bruit blanc à la texture rendue .. La solution consiste à utiliser deux textures: le bruit blanc d'origine et le son blanc pur, comme celui-ci: wiki white noise

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";

Le fragment partagé contient le paramètre uNoiseFactor qui est mis à jour à chaque rendu par l'application principale:

float noiseValue = (float)(mRand.nextInt() % 1000)/1000;
int noiseFactorUniformHandle = GLES20.glGetUniformLocation( mProgram, "sNoiseTextureUnit");
GLES20.glUniform1f(noiseFactorUniformHandle, noiseFactor);
0
klimletov

hash: De nos jours webGL2.0 existe, donc des entiers sont disponibles dans (w) GLSL .-> pour un hachage portable de qualité (à un coût similaire à celui des laids hachés), nous pouvons maintenant utiliser des techniques de hachage "sérieuses" .IQ en a implémenté dans https://www.shadertoy.com/view/XlXcW4 (et plus)

Par exemple.:

  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));
}
0
Fabrice NEYRET

J'ai traduit une des implémentations Java de Ken Perlin en GLSL et je l'ai utilisé dans quelques projets sur ShaderToy.

Voici l'interprétation que j'ai faite du GLSL:

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);
}

Je l'ai traduit de l'annexe B du chapitre 2 de Noise Hardware de Ken Perlin à cette source:

https://www.csee.umbc.edu/~olano/s2002c36/ch02.pdf

Voici une ombre publique que j'ai faite sur Shader Toy qui utilise la fonction de bruit postée:

https://www.shadertoy.com/view/3slXzM

Je ne me souviens plus très bien, mais je pense que depuis 1.0/noise, ce bruit est simplex au lieu de vrai Perlin Noise.

Parmi les autres bonnes sources que j'ai trouvées au sujet du bruit pendant mes recherches, citons:

https://thebookofshaders.com/11/

https://mzucker.github.io/html/perlin-noise-math-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

Je recommande fortement le livre des shaders, car il fournit non seulement une excellente explication interactive du bruit, mais également d'autres concepts de shader.

0
Andrew Meservy

Utilisez ceci:

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);
}

N'utilisez pas ceci:

float Rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

Vous pouvez trouver l'explication dans Améliorations apportées au canonique GLSL Rand () canonique pour OpenGL ES 2.

0
hoangdado