GLSL的随机/噪声功能


179

由于GPU驱动程序供应商通常不愿意noiseX在GLSL中实现,因此我正在寻找“图形随机化瑞士军刀”实用程序功能集,最好对其进行优化以在GPU着色器中使用。我更喜欢GLSL,但是可以编写任何语言的代码,我可以自己将其翻译为GLSL。

具体来说,我期望:

a)伪随机函数 -从M维种子计算出的[-1,1]或[0,1]上的N维均匀分布(理想情况下为任何值,但我可以限制种子设为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)像Perlin 噪声这样的连续噪声 -同样是N维,分布均匀,具有一组受约束的值,而且看起来不错(一些配置外观(如Perlin级别)的选项也可能有用)。我希望像这样的签名:

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

我对随机数生成理论不太了解,因此我非常渴望获得预制的解决方案,但是我也希望得到诸如“这里有一个非常好的,高效的一维rand()”之类的答案,让我解释一下您如何在其上制作一个良好的N维rand()...”

Answers:


263

对于看起来很简单的伪随机事物,我使用在互联网上某个地方找到的这个单件套:

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

您还可以使用任何喜欢的PRNG生成噪波纹理,然后以常规方式上传并在着色器中采样值;如果您愿意,我可以在以后提取代码示例。

另外,请查阅Stefan Gustavson提供的有关Perlin和Simplex noise的GLSL实现的文件


14
你怎么用vec2 co?是范围吗?种子?
罗斯

12
提防使用此算法的低精度浮点片段着色器(例如S3的ARM Mali):stackoverflow.com/questions/11293628/…。该github.com/ashima/webgl-noise项目似乎不具有lowp问题。
PT

4
FWIW,功能在此描述中更详细地讨论在这里
Loomchild 2013年

3
仅供参考:该功能的分布非常糟糕。
塔拉2014年

3
我是GLSL中的newb,谁能解释为什么co.xy使用它,而不是co
凯林2015年

83

在我看来,您可以使用简单的整数哈希函数,然后将结果插入浮点数的尾数。IIRC GLSL规范可保证32位无符号整数和IEEE binary32浮点表示形式,因此它应该是完全可移植的。

我刚刚尝试了一下。结果非常好:我尝试的每个输入看起来都完全是静态的,根本没有可见的模式。相反,在给定相同输入的情况下,流行的sin / fract片段在我的GPU上具有相当明显的对角线。

缺点之一是它需要GLSL v3.30。尽管它看起来足够快,但是我还没有从经验上量化它的性能。对于HD5870上的vec2版本,AMD的Shader Analyzer声称每个时钟13.33像素。正弦/分数片段的每个时钟与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 );
}

屏幕截图:

在static.frag中输出random(vec3)

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


17
+1是个好主意和实现。我会质疑这种说法,因为有256种颜色且平均值为127,所以分布必须均匀(严格意义上来说)。它可能是统一的,但我认为我们还不知道。例如,钟形曲线分布可能具有相同的平均数和颜色数,但不一致。
LarsH 2014年

出于@LarsH给出的原因,对此进行了投票。
Autumnsault 2014年

好吧,对于大多数不需要统一性的应用程序来说已经足够了。:-)
itmuckel

5
根据我对直方图的理解,它看起来非常均匀。...我想说它对于大多数需要均匀性的应用程序也足够好。(唯一看起来比其他值少的值是0和255)
leviathanbadger

谢谢。我的机率是生锈的。看完GCN指令集之后,这在较新的硬件上应该非常快,因为它们直接在指令集中支持位域操作。我所做的测试是在较旧的硬件上进行的。
空间

73

Gustavson的实现使用1D纹理

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

Ashima和我本人的Ian McEwan的新版本不使用纹理,但是在具有大量纹理带宽的典型台式机平台上,其运行速度仅为其一半。在移动平台上,无纹理版本可能会更快,因为纹理化通常是一个很大的瓶颈。

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

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

这里是无纹理和使用纹理的两种噪声的集合(仅使用2D纹理):

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

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


4
是的,我指的是@dep链接到的在davidcornette.com上的代码,它确实使用1D纹理:glBindTexture(GL_TEXTURE_1D, *texID);等等。由于您引用了我的答案,因此不清楚“您提供的链接”是什么意思但该答案并未链接到您的实现。我将更新我的答案,以阐明我的意思并反映您提供的新信息。在下载旧版本时将人们描述为“坚持”是一种扭曲,您不会相信。
LarsH 2012年

1
PS:您可能想写信给David Cornette(他在davidcornette.com上有联系信息),并要求他更改davidcornette.com/glsl/links.html上的链接以链接到您的源存储库。我也会给他发电子邮件。
LarsH 2012年

1
PPS您能澄清一下,哪个版本仅使用8位2D纹理吗?听起来对于某些平台来说,这可能是一个不错的选择……
LarsH 2012年

31

金噪声

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

立即在浏览器中查看“金噪声”!

在此处输入图片说明

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

在此处输入图片说明

由于没有提供种子,因此@appas函数也不完整(uv不是种子-每帧都相同),并且不适用于低精度芯片组。默认情况下,“金噪声”以低精度运行(快得多)。


感谢您发布此信息。您是否考虑发布可运行的版本,例如在shadertoy.com上,以便人们可以在浏览器中试用?
LarsH 2015年

@snb Shadertoy.com本月正在进行维护,请耐心等待。我也清楚地在代码中记录了对非理性种子值的要求。由于金噪声返回标量,因此用它构造向量很简单,并且也记录在代码中。
Dominic Cerisano

7
我认为这与其他噪声功能没有任何不同。您如何证明它具有特殊属性。仅仅因为您使用一堆无理数并不特别。
M.kazem Akhgary,

2
@Dominic:“它具有比同类功能更好的分发功能”:这必须得到证明。tan()确实病情严重。pi / 2附近的tan()和零附近的sqrt()都可能在不同的硬件上产生不同的结果,因为所有fract(linear * big)都是基于较低有效位。输入值的小或高也会对其产生影响。另外,根据位置的不同,位的动态变化也可能很大。
Fabrice NEYRET

1
注意:如今,GLSL具有整数,因此在需要具有相似性能的质量分布(和动态)时,不再有任何理由不使用“严重的”基于int的哈希生成器。(非常低端的设备除外)。
Fabrice NEYRET

12

这里也描述一个不错的实现McEwan和@StefanGustavson,看起来像Perlin噪声,但是“不需要任何设置,即不需要纹理或统一数组。只需将其添加到着色器源代码中,然后在任何需要的地方调用它”。

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


1
这是对OP的b)噪声类型要求的最佳答案!这是直接链接github.com/ashima/webgl-noise。有2d,3d和4d版本可以作为GLSL 120代码使用。
user362515 2015年

3

使用这个:

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()的改进中找到说明。


我浏览了这篇文章,但仍不确定是modpi的3.14 左右吗?
Kaan E.

2

刚刚发现此版本的3d噪声适用于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

1
那么,Gold Noise(上述)显然是最快的,因为它的操作少得多,并且仅执行一次哈希-该哈希将其哈希函数调用8次,同时执行嵌套的线性插值(lerps)。另外,这也具有较差的分布,特别是在精度较低的情况下。
多米尼克·塞里萨诺

1
好的,这是来自Inigo Quilez的shadertoh的Perlin噪声类型图。好的代码多米尼克病检查它l8r
可理解的

@Fabrice您似乎不了解OP的问题,我的答案,我的代码或我的评论。.Gold Noise在OP定义中是连续的-它接受uv和种子,并通过提供着色器对其进行证明。关于您的评论的所有内容都是错误的。您会使哈希函数与伪随机噪声函数混淆不清。她们不一样。噪声函数不需要生成唯一的标识符,例如哈希函数(哈希的实际整个点)。
Dominic Cerisano

多米尼克(Dominic),请多读并了解更多,然后再声明不是您所认为的术语。这些术语不仅非常精确,而且在文献中定义明确,而且我在该领域工作,而且OP还证明了他通过下面给出的示例可以理解这些术语。提示:“连续” +“噪声” +“像Perlin”。en.wikipedia.org/wiki/Perlin_noise
Fabrice NEYRET

仅在添加循环子句的情况下才是连续的,由于位舍入,尤其是对于图形,许多噪声函数以某种方式循环并降级。伙计们,你们之间的沟通中断了,请您花时间进行重要的研究。
com.prehensible

1

1d Perlin的锯齿状笔直版本,本质上是随机的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);
}

我还在sharttoy所有者inigo quilez perlin教程网站以及voronoi等网站上发现了1-2-3-4d perlin杂音,他为它们提供了完整的快速实现和代码。


1

哈希:如今,webGL2.0可用,因此(w)GLSL中可以使用整数。->对于高质量的便携式哈希(成本与丑陋的浮点哈希相似),我们现在可以使用“严重”哈希技术。IQ在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));
}

0

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

0

我已经将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上使用公开的噪波功能所做的公共阴影:

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中可用的某些硬件加速功能,可能能够优化翻译后的代码。如果最终执行此操作,将更新此帖子。


同样,我很确定Perlin / Simplex Noise仍然是伪随机的。据我回忆,有趣的是,您可以将噪声分层并“缩放”到不同的级别,以使其看起来非常无缝。不要在这方面引用我,但要考虑一下。
Andrew Meservy

@Zibri不幸的是,我对C或.sh的直接命令不是很熟悉。但是看起来函数只是一个伪随机数生成器,而不是噪声函数。还请记住,glsl像素着色器直接在gpu上运行。您将无法访问任何这些额外的库或CPU能力可能在C.可
安德鲁Meservy

《着色器手册》很好地解释了“单纯形噪声”是由于倾斜网格和每点不必要的计算而成为Perlin Noise的更有效版本。绝对值得一读。
Andrew Meservy

另请参阅有关分形布朗运动和伏龙的章节
Andrew Meservy

Andrew Meservy:不需要库...我的噪声函数非常简单:2个64位整数是状态x(n)和x(n-1)。简单快捷的公式是x(n + 1)= ROTR( x(n)+ x(n-1),8)。如果克隆我的git并运行它,您将看到它的运行。
Zibri
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.