代码之家  ›  专栏  ›  技术社区  ›  Kos

GLSL的随机/噪声函数

  •  157
  • Kos  · 技术社区  · 14 年前

    因为GPU驱动程序供应商通常不会费心去实现 noiseX 在GLSL,我要找一个 “图形随机化瑞士军刀” 效用函数集,最好优化为在GPU着色器中使用。我更喜欢GLSL,但是编写任何语言都可以,我可以自己翻译成GLSL。

    具体来说,我希望:

    (一) 伪随机函数 -N维,均匀分布在[-1,1]或[0,1]以上,由M维种子计算(理想情况下是任意值,但我同意将种子限制在0..1,以获得均匀结果分布)。类似于:

    float random  (T seed);
    vec2  random2 (T seed);
    vec3  random3 (T seed);
    vec4  random4 (T seed);
    // T being either float, vec2, vec3, vec4 - ideally.
    

    (二) 连续噪声 像Perlin噪声一样-同样,N维,+-均匀分布,有约束的值集,看起来很好(一些配置外观的选项,像Perlin级别也很有用)。我希望签名如下:

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

    我对随机数生成理论不太感兴趣,所以我非常希望 预制溶液 ,但我也很感激 “这里有一个非常好的、高效的1D rand(),让我来解释一下如何在它上面生成一个好的N维rand()。” .

    10 回复  |  直到 14 年前
        1
  •  251
  •   appas    14 年前

    对于看起来很简单的伪随机的东西,我使用我在互联网上找到的一行代码:

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

    你也可以使用任何你喜欢的PRNG生成一个噪波纹理,然后以正常的方式上传并在你的着色器中采样值;如果你愿意,我可以稍后挖掘一个代码示例。

    还有,退房 this file 对于Perlin和Simplex噪声的GLSL实现,由Stefan Gustavson完成。

        2
  •  71
  •   finnw    11 年前

    Gustavson的实现使用1D纹理

    不,从2005年开始就没有了。只是人们坚持下载旧版本。您提供的链接上的版本仅使用8位二维纹理。

    Ashima和我自己的Ian McEwan的新版本没有使用纹理,但是在具有大量纹理带宽的典型桌面平台上运行的速度大约是其速度的一半。在移动平台上,无纹理版本可能更快,因为纹理通常是一个重要的瓶颈。

    我们积极维护的源代码库是:

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

    下面是无纹理和使用噪波版本的纹理的集合(仅使用二维纹理):

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

    如果您有任何具体问题,请直接给我发电子邮件(我的电子邮件地址可以在 classicnoise*.glsl 来源。)

        3
  •  70
  •   Spatial    11 年前

    我突然想到,可以使用一个简单的整数散列函数,并将结果插入浮点的尾数。IIRC GLSL规范保证32位无符号整数和IEEE二进制32浮点表示,因此它应该是完全可移植的。

    我刚才试了一下。结果非常好:我尝试的每个输入看起来都是静态的,根本没有可见的模式。相比之下,流行的sin/fractsnippet在我的GPU上有相当明显的对角线,输入相同。

    一个缺点是它需要GLSL v3.30。虽然它看起来足够快,但我还没有根据经验来量化它的性能。AMD的Shader Analyzer声称HD5870上的vec2版本每时钟13.33像素。与sin/fract片段的每个时钟16像素形成对比。所以肯定是慢了一点。

    这是我的实现。我把它放在不同的思想排列中,以便更容易从中派生出自己的函数。

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

    截图:

    Output of random(vec3) in static.frag

    我在一个图像编辑程序中检查了屏幕截图。有256种颜色,平均值是127,这意味着分布是均匀的,覆盖了预期的范围。

        4
  •  24
  •   Dominic Cerisano    5 年前

    黄金噪音

    // Gold Noise ©2015 dcerisano@standard3d.com
    // - based on the Golden Ratio
    // - uniform normalized distribution
    // - fastest static noise generator function (also runs at low precision)
    
    float PHI = 1.61803398874989484820459;  // Φ = Golden Ratio   
    
    float gold_noise(in vec2 xy, in float seed){
           return fract(tan(distance(xy*PHI, xy)*seed)*xy.x);
    }
    

    See Gold Noise in your browser right now!

    enter image description here

    截至2017年9月9日,@appas'答案中,此函数改进了当前函数的随机分布:

    enter image description here

    @appas函数也不完整,因为没有提供种子(uv不是每个帧的种子都一样),并且不能与低精度芯片组一起工作。默认情况下,Gold Noise以低精度运行(更快)。

        5
  •  11
  •   Community miroxlav    7 年前

    还有一个很好的实现 here 由McEwan和@StefanGustavson设计,看起来像Perlin noise,但“不需要任何设置,即不需要纹理或均匀阵列。只需将其添加到着色器源代码中,并在任何需要的地方调用它”。

    这非常方便,特别是考虑到Gustavson的早期实现@dep链接到使用1D纹理,即 not supported in GLSL ES (WebGL的着色语言)。

        6
  •  3
  •   hoangdado    5 年前

    请使用:

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

    不要使用这个:

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

    你可以在 Improvements to the canonical one-liner GLSL rand() for OpenGL ES 2.0

        7
  •  2
  •   bandybabboon    10 年前

    刚刚发现这个版本的GPU 3d噪音,尽管它是最快的一个:

    #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
    
        8
  •  1
  •   eyllanesc RAHUL KUMAR    6 年前

    一种直的,锯齿状的1d珀林,本质上是一个随机的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);
    }
    

    我还在shadertoy的所有者inigo quilez perlin教程网站和voronoi等网站上找到了1-2-3-4d perlin noise,他有完全快速的实现和代码。

        9
  •  1
  •   Fabrice NEYRET    6 年前

    搞砸: 现在webGL2.0已经存在,所以整数可以在(w)GLSL中使用。 ->对于高质量的可移植散列(成本与丑陋的浮点散列相似),我们现在可以使用“严肃的”散列技术。 智商在 https://www.shadertoy.com/view/XlXcW4 (及更多)

    例如。:

      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));
    }
    
        10
  •  0
  •   eyllanesc RAHUL KUMAR    6 年前

    请参见下面的示例如何向渲染纹理添加白噪声。 解决方案是使用两种纹理:原始和纯白色噪波,如下所示: 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";
    

    共享的片段包含参数uNoiseFactor,主应用程序在每次呈现时都会更新该参数:

    float noiseValue = (float)(mRand.nextInt() % 1000)/1000;
    int noiseFactorUniformHandle = GLES20.glGetUniformLocation( mProgram, "sNoiseTextureUnit");
    GLES20.glUniform1f(noiseFactorUniformHandle, noiseFactor);
    
        11
  •  0
  •   Andrew Meservy    5 年前

    我已经将Ken Perlin的一个Java实现翻译成GLSL,并在ShaderToy上的几个项目中使用了它。

    以下是我对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);
    }
    

    我从Ken Perlin噪声硬件第2章的附录B中翻译过来:

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

    下面是我在Shader Toy上使用posted noise函数的公共阴影:

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

    我在研究期间发现的关于噪音的其他一些好的来源包括:

    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

    我强烈推荐《着色器之书》,因为它不仅提供了一个很好的噪声交互式解释,而且还提供了其他着色器概念。

    编辑:

    可能可以通过使用GLSL中的一些硬件加速功能来优化翻译的代码。如果我最终这么做的话,我会更新这篇文章的。