由于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()……”.


当前回答

一定要用这个:

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

您可以在改进OpenGL ES 2.0的规范一行程序GLSL rand()中找到解释

其他回答

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

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

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

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

我也有同样的问题,我需要在WebGL 1.0中实现它,所以我不能使用之前回答中给出的一些例子。我尝试了前面提到的Gold Noise,但是PHI的使用并不适合我。(距离(xy * PHI, xy) *种子就等于长度(xy) * (1.0 - PHI) *种子,所以我不明白当它直接乘以种子时,PHI的魔力应该如何发挥作用?

不管怎样,我做了类似的事情,只是没有PHI,而是在另一个地方添加了一些变化,基本上我取xy与框架外的某个随机点之间的距离的tan,然后乘以xy与另一个这样的随机点之间的距离,位于左下角(所以这些点之间没有偶然的匹配)。在我看来还不错。单击生成新的帧。

(function main() { const dim = [512, 512]; twgl.setDefaults({ attribPrefix: "a_" }); const gl = twgl.getContext(document.querySelector("canvas")); gl.canvas.width = dim[0]; gl.canvas.height = dim[1]; const bfi = twgl.primitives.createXYQuadBufferInfo(gl); const pgi = twgl.createProgramInfo(gl, ["vs", "fs"]); gl.canvas.onclick = (() => { twgl.bindFramebufferInfo(gl, null); gl.useProgram(pgi.program); twgl.setUniforms(pgi, { u_resolution: dim, u_seed: Array(4).fill().map(Math.random) }); twgl.setBuffersAndAttributes(gl, pgi, bfi); twgl.drawBufferInfo(gl, bfi); }); })(); <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script> <script id="vs" type="x-shader/x-vertex"> attribute vec4 a_position; attribute vec2 a_texcoord; void main() { gl_Position = a_position; } </script> <script id="fs" type="x-shader/x-fragment"> precision highp float; uniform vec2 u_resolution; uniform vec2 u_seed[2]; void main() { float uni = fract( tan(distance( gl_FragCoord.xy, u_resolution * (u_seed[0] + 1.0) )) * distance( gl_FragCoord.xy, u_resolution * (u_seed[1] - 2.0) ) ); gl_FragColor = vec4(uni, uni, uni, 1.0); } </script> <canvas></canvas>

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

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

在2010年首次发布这个问题之后,在良好的随机函数和硬件支持方面发生了很大的变化。

从今天的角度来看,这个算法抽取的随机数的一致性很差。均匀性受到输入值大小的很大影响,当从输入值中采样时,例如射线/路径跟踪应用程序,可见的工件/模式将变得明显。

There have been many different functions (most of them integer hashing) being devised for this task, for different input and output dimensionality, most of which are being evaluated in the 2020 JCGT paper Hash Functions for GPU Rendering. Depending on your needs you could select a function from the list of proposed functions in that paper and simply from the accompanying Shadertoy. One that isn't covered in this paper but that has served me very well without any noticeably patterns on any input magnitude values is also one that I want to highlight.

其他类型的算法使用低差异序列来绘制伪随机数,例如带有Owen-Nayar置乱的Sobol序列。Eric Heitz在这一领域做了一些惊人的研究,以及他在Screen Space论文中将蒙特卡罗误差分布为蓝色噪声的低差异采样器。 另一个例子是(迄今为止最新的)JCGT论文基于实用哈希的Owen置乱,它将Owen置乱应用于不同的哈希函数(即Laine-Karras)。

然而,其他类别使用算法来产生具有理想频谱的噪声模式,例如蓝色噪声,它特别“取悦”眼睛。

(我意识到好的StackOverflow答案应该提供算法作为源代码,而不是链接,因为那些可能会中断,但现在有太多不同的算法,我打算把这个答案作为今天已知的好算法的总结)

刚找到这个版本的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