Funzioni casuali / rumore per GLSL

Poiché i fornitori di driver GPU di solito non si preoccupano di implementare noiseX in GLSL, sto cercando un set di funzioni di utilità “coltellino svizzero di casualizzazione della grafica” , preferibilmente ottimizzato per l’uso all’interno degli shader GPU. Preferisco il GLSL, ma il codice che qualsiasi linguaggio farà per me, sto bene con la traduzione da solo a GLSL.

Specificamente, mi aspetterei:

a) Funzioni pseudo-casuali – N-dimensionale, distribuzione uniforms su [-1,1] o su [0,1], calcasting da seme M-dimensionale (essendo idealmente qualsiasi valore, ma sto bene avendo il seme trattenuto a, ad esempio, 0..1 per una distribuzione uniforms dei risultati). Qualcosa di simile a:

 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) Rumore continuo come Perlin Noise – di nuovo, N-dimensionale, + – distribuzione uniforms, con set di valori vincolati e, beh, in buone condizioni (alcune opzioni per configurare l’aspetto come i livelli di Perlin potrebbero essere anche utili). Mi aspetterei firme come:

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

Non sono molto interessato alla teoria della generazione dei numeri casuali, quindi mi piacerebbe molto per una soluzione pre-fatta , ma apprezzerei anche le risposte come “Ecco un ottimo 1D rand () molto efficiente, e lascia che ti spieghi su come creare un buon N-dimensional rand () sopra di esso … “ .

Per cose pseudocasuali molto semplici, io uso questo oneliner che ho trovato su internet da qualche parte:

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

Puoi anche generare una trama di rumore usando qualsiasi PRNG che ti piace, quindi carica questo nel modo normale e campiona i valori nello shader; Posso scavare un esempio di codice più tardi, se lo desideri.

Inoltre, dai un’occhiata a questo file per le implementazioni GLSL di Perlin e Simplex noise, di Stefan Gustavson.

L’implementazione di Gustavson utilizza una trama 1D

No, non dal 2005, è solo che le persone insistono nel scaricare la vecchia versione. La versione sul link che hai fornito utilizza solo trame 2D a 8 bit.

La nuova versione di Ian McEwan di Ashima e io non usa una texture, ma funziona a circa metà della velocità su piattaforms desktop tipiche con molta larghezza di banda. Sulle piattaforms mobili, la versione senza trama potrebbe essere più veloce perché la texturing è spesso un collo di bottiglia significativo.

Il nostro repository sorgente triggersmente mantenuto è:

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

Qui è disponibile una raccolta di entrambe le versioni di rumore senza trama e texture (utilizzando solo texture 2D):

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

Se avete domande specifiche, sentitevi liberi di inviarmi un’e-mail direttamente (il mio indirizzo e-mail può essere trovato nelle fonti classicnoise*.glsl .)

Mi viene in mente che potresti usare una semplice funzione di hash di interi e inserire il risultato nella mantissa di un float. IIRC la specifica GLSL garantisce interi senza segno a 32 bit e rappresentazione float IEEE binary32 quindi dovrebbe essere perfettamente portatile.

Ho fatto una prova proprio ora. I risultati sono ottimi: sembra esattamente statico con ogni input che ho provato, nessun pattern visibile. Al contrario, il popolare snippet sin / fract ha linee diagonali abbastanza pronunciate sulla mia GPU date le stesse entrate.

Uno svantaggio è che richiede GLSL v3.30. E anche se sembra abbastanza veloce, non ho quantificato empiricamente le sue prestazioni. L’analizzatore di Shader di AMD rivendica 13,33 pixel per orologio per la versione vec2 su un HD5870. Contrasto con 16 pixel per orologio per lo snippet sin / fract. Quindi è certamente un po ‘più lento.

Ecco la mia implementazione. L’ho lasciato in varie permutazioni dell’idea per rendere più facile derivare le tue funzioni da.

 /* 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( vx ^ hash(vy) ); } uint hash( uvec3 v ) { return hash( vx ^ hash(vy) ^ hash(vz) ); } uint hash( uvec4 v ) { return hash( vx ^ hash(vy) ^ hash(vz) ^ hash(vw) ); } // 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 ); } 

Immagine dello schermo:

Uscita casuale (vec3) in static.frag

Ho ispezionato lo screenshot in un programma di modifica delle immagini. Ci sono 256 colors e il valore medio è 127, il che significa che la distribuzione è uniforms e copre l'intervallo previsto.

Rumore d’oro

 // 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) 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(tan(distance(coordinate*(seed+PHI), vec2(PHI, PI)))*SQ2); } 

Guarda Gold Noise nel tuo browser adesso!

inserisci la descrizione dell'immagine qui

Questa funzione ha migliorato la distribuzione casuale rispetto alla funzione corrente nella risposta di @appas a partire dal 9 settembre 2017:

inserisci la descrizione dell'immagine qui

Anche la funzione @appas è incompleta, dato che non ci sono seed forniti (uv non è un seed – lo stesso per ogni frame), e non funziona con chipset di bassa precisione. Il rumore dell’oro viene eseguito con bassa precisione per impostazione predefinita (molto più veloce).

C’è anche una bella implementazione qui descritta da McEwan e @StefanGustavson che assomiglia al rumore di Perlin, ma “non richiede alcuna configurazione, cioè non trame o array uniformi. Basta aggiungerlo al codice sorgente dello shader e chiamarlo dove vuoi”.

Ciò è molto utile, soprattutto dato che l’implementazione precedente di Gustavson, a cui @dep è collegata, utilizza una trama 1D, che non è supportata in GLSL ES (il linguaggio shader di WebGL).

Ho appena trovato questa versione del rumore 3D per GPU, di conseguenza è la più veloce disponibile:

 #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 = px + py*57.0 + 113.0*pz; return lerp(lerp(lerp( hash(n+0.0), hash(n+1.0),fx), lerp( hash(n+57.0), hash(n+58.0),fx),fy), lerp(lerp( hash(n+113.0), hash(n+114.0),fx), lerp( hash(n+170.0), hash(n+171.0),fx),fy),fz); } #endif 

Una versione dritta e frastagliata di 1d Perlin, essenzialmente uno zigzag casuale a forma di lfo.

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

Ho anche trovato il rumore perlinico 1-2-3-4d su shadertoy proprietario inigo quilez perlin sito web tutorial, e voronoi e così via, ha implementazioni e codici completi per loro.

Vedi sotto un esempio su come aggiungere rumore bianco alla texture renderizzata. La soluzione è utilizzare due trame: rumore bianco originale e puro, come questo: rumore bianco 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"; 

Il frammento condiviso contiene il parametro uNoiseFactor, che viene aggiornato su ogni rendering dall’applicazione principale:

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