由于GPU驱动供应商通常不会费心在GLSL中实现noiseX,我正在寻找一个“图形随机化瑞士军刀”实用函数集,最好是在GPU着色器中优化使用。我更喜欢GLSL,但是任何语言的代码对我来说都可以,我可以把它翻译成GLSL。

具体来说,我希望:

a)伪随机函数- 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.

b)连续的噪声,比如柏林噪声——同样是n维,+-均匀分布,有约束的值集,看起来不错(一些配置外观的选项,比如柏林电平也很有用)。我希望签名是这样的:

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

我对随机数生成理论不太感兴趣,所以我最渴望的是一个现成的解决方案,但我也很喜欢这样的回答:“这里有一个非常好的,高效的1D rand(),让我解释一下如何在它的基础上制作一个好的n维rand()……”.


当前回答

这里还有一个由McEwan和@StefanGustavson描述的很好的实现,看起来像柏林噪声,但“不需要任何设置,即不需要纹理或统一数组。只需将它添加到你的着色器源代码,并在任何你想要的地方调用它”。

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

其他回答

我已经将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's Noise Hardware的第二章附录B中翻译过来:

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

下面是我在Shader Toy上做的一个公共阴影,它使用了张贴噪音功能:

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中提供的一些硬件加速函数来优化翻译后的代码。如果我最终这样做了,我会更新这篇文章。

对于非常简单的伪随机的东西,我使用这个在线程序,我在互联网上找到的地方:

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

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

另外,查看Stefan Gustavson的Perlin和Simplex噪声的GLSL实现文件。

这里还有一个由McEwan和@StefanGustavson描述的很好的实现,看起来像柏林噪声,但“不需要任何设置,即不需要纹理或统一数组。只需将它添加到你的着色器源代码,并在任何你想要的地方调用它”。

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

Gustavson的实现使用1D纹理

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

我和阿希玛的Ian McEwan共同开发的新版本没有使用纹理,但是在典型的桌面平台上,纹理带宽很大,运行速度只有它的一半左右。在移动平台上,无纹理版本可能更快,因为纹理通常是一个重要的瓶颈。

我们积极维护的源存储库是:

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

这里是无纹理和使用纹理的噪音版本的集合(只使用2D纹理):

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

如果您有任何具体的问题,请直接给我发电子邮件(我的电子邮件地址可以在经典噪声*中找到)。glsl来源。)

请看下面的例子如何添加白噪声渲染纹理。 解决方案是使用两种纹理:原始白噪声和纯白噪声,就像这个: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";

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

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