Funkcje Random / noise dla GLSL

Ponieważ dostawcy sterowników GPU zwykle nie trudzą się implementacją noiseX w GLSL, Szukam zestawu funkcji użytkowych "graphics randomization swiss army knife", najlepiej zoptymalizowanego do użycia w shaderach GPU. Wolę GLSL, ale kod dowolny język zrobi dla mnie, jestem w porządku z tłumaczeniem go na własną rękę do GLSL.

W szczególności oczekiwałbym:

A) Funkcje pseudolosowe - N-wymiarowy, równomierny rozkład nad [-1,1] lub ponad [0,1], obliczony z M-dimensional seed (najlepiej jest jakakolwiek wartość, ale nie przeszkadza mi, że seed jest ograniczony do, powiedzmy, 0..1 dla jednolitego rozkładu wyników). Coś w stylu:

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) ciągły szum jak szum Perlina-znowu N - wymiarowy, + - równomierny rozkład, z ograniczonym zestawem wartości i dobrze wyglądający (przydałyby się też opcje konfiguracji wyglądu, takie jak poziomy Perlina). Spodziewałbym się takich podpisów jak:

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

Nie przepadam za przypadkowymi liczbami teoria generacji, więc z niecierpliwością wybierałbym gotowe rozwiązanie , ale byłbym również wdzięczny za odpowiedzi typu " Oto bardzo dobry, wydajny rand 1D () i pozwól, że wyjaśnię Ci, jak zrobić dobry N-wymiarowy rand ()..." .

Author: Kos, 2010-11-17

9 answers

Do bardzo prostych pseudorandomowych rzeczy używam tego onelinera, który znalazłem gdzieś w internecie:

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

Możesz również wygenerować teksturę szumu używając dowolnego PRNG, a następnie przesłać ją w normalny sposób i wypróbować wartości w swoim shaderze.

Sprawdź również ten plik dla implementacji GLSL Perlina i szumu Simplex autorstwa Stefana Gustavsona.

 234
Author: appas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-25 09:12:38

Implementacja Gustavsona wykorzystuje teksturę 1D

Nie, Nie od 2005 roku. Chodzi o to, że ludzie nalegają na pobranie starej wersji. Wersja, która znajduje się na podanym linku, używa tylko 8-bitowych tekstur 2D.

Nowa wersja Ian McEwan z Ashima i ja nie używa tekstury, ale działa z około połowy prędkością na typowych platformach desktopowych z dużą przepustowością tekstury. Na platformach mobilnych wersja textureless może być szybsza, ponieważ teksturowanie jest często istotnym wąskim gardłem.

Nasze aktywnie utrzymywane repozytorium źródłowe to:

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

Zbiór zarówno teksturowych, jak i teksturowych wersji szumu jest tutaj (używając tylko tekstur 2D):

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

Jeśli masz jakieś konkretne pytania, napisz do mnie bezpośrednio (mój adres e-mail można znaleźć w classicnoise*.glsl źródła.)

 67
Author: Stefan Gustavson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-01 03:18:09

Przyszło mi do głowy, że możesz użyć prostej funkcji skrótu liczb całkowitych i wstawić wynik do mantysy float ' a. IIRC Specyfikacja GLSL gwarantuje 32-bitowe niepodpisane liczby całkowite i reprezentację zmiennoprzecinkową IEEE binary32, więc powinna być idealnie przenośna.

Przed chwilą spróbowałem. Wyniki są bardzo dobre: wygląda dokładnie jak statyczny z każdym wejściem próbowałem, żadnych widocznych wzorców w ogóle. W przeciwieństwie do popularnego sin / fract snippet ma dość wyraźne ukośne linie na moim GPU podane te same wejścia.

Wadą jest to, że wymaga GLSL v3. 30. I chociaż wydaje się wystarczająco szybki, nie podałem empirycznie jego wydajności. AMD Shader Analyzer twierdzi, 13.33 pikseli na zegarze dla wersji vec2 na HD5870. Kontrast z 16 pikselami na zegar dla fragmentu sin/fract. Więc z pewnością jest trochę wolniejszy.

Oto moja implementacja. Zostawiłem go w różnych permutacjach idei, aby ułatwić czerpanie własnych funkcji od.
/*
    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 );
}

Zrzut ekranu:

Wyjście losowe (vec3) w statycznym.frag

Sprawdzałem zrzut ekranu w programie do edycji obrazów. Istnieje 256 kolorów, a średnia wartość wynosi 127, co oznacza, że rozkład jest jednolity i obejmuje oczekiwany zakres.

 58
Author: Spatial,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-04 23:40:13

Gold Noise

// 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)

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

Zobacz Gold Noise w swojej przeglądarce już teraz!

Tutaj wpisz opis obrazka

Ta funkcja poprawiła rozkład losowy nad bieżącą funkcją w odpowiedzi @ appas na dzień 9 września 2017:

Tutaj wpisz opis obrazka

Funkcja @ appas jest również niekompletna, ponieważ nie ma dostarczonego ziarna (uv nie jest ziarnem-takie same dla każdej ramki) i nie działa z chipsetami o niskiej precyzji. Gold Noise działa domyślnie z niską precyzją (znacznie szybciej).

 15
Author: Dominic Cerisano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-31 18:34:51

Jest też Ładna implementacja opisana tutaj przez McEwana i @StefanGustavson, która wygląda jak szum Perlina, ale "nie wymaga żadnej konfiguracji, tzn. nie wymaga tekstur ani jednolitych tablic. Po prostu dodaj go do kodu źródłowego shader 'a i zadzwoń do niego, gdzie chcesz".

Jest to bardzo przydatne, szczególnie biorąc pod uwagę, że wcześniejsza implementacja Gustavsona, z którą połączony jest @dep, używa tekstury 1D, która jest nieobsługiwana w GLSL ES (Język cieniowania WebGL).

 11
Author: LarsH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:19

Właśnie znalazłem tę wersję szumu 3D dla GPU, chyba najszybszą dostępną:

#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
Author: com.prehensible,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-06 13:49:31

Prosta, postrzępiona wersja Perlina 1d, w zasadzie losowy zygzak 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);
}

Znalazłem również 1-2-3-4D Perlin noise na stronie shadertoy właściciela Inigo quilez perlin tutorial, i voronoi i tak dalej, ma pełne szybkie implementacje i kody do nich.

 0
Author: com.prehensible,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-16 10:12:37

Poniżej znajduje się przykład dodawania białego szumu do renderowanej tekstury. Rozwiązaniem jest użycie dwóch tekstur: original i pure white noise, jak Ta: 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";

Udostępniony fragment zawiera parametr uNoiseFactor, który jest aktualizowany przy każdym renderowaniu przez główną aplikację:

float noiseValue = (float)(mRand.nextInt() % 1000)/1000;
int noiseFactorUniformHandle = GLES20.glGetUniformLocation( mProgram, "sNoiseTextureUnit");
GLES20.glUniform1f(noiseFactorUniformHandle, noiseFactor);
 0
Author: klimletov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-16 10:13:10

Hash: Obecnie istnieje webGL2.0, więc liczby całkowite są dostępne w (w)GLSL. -> dla wysokiej jakości przenośnego hasha (za podobną cenę niż ugly float hashes) możemy teraz użyć "poważnych" technik mieszania. IQ zaimplementował niektóre w https://www.shadertoy.com/view/XlXcW4 (i więcej)

Np.:

  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
Author: Fabrice NEYRET,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-06 15:28:03