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


当前回答

散列: 现在有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));
}

其他回答

一定要用这个:

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()中找到解释

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

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

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

我刚才试过了。结果非常好:我尝试的每个输入看起来都像静态的,根本没有可见的模式。相比之下,在相同的输入条件下,流行的sin/fract代码片段在我的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 );
}

截图:

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

黄金的噪音

// Gold Noise ©2015 dcerisano@standard3d.com
// - based on the Golden Ratio
// - uniform normalized distribution
// - fastest static noise generator function (also runs at low precision)
// - use with indicated fractional seeding method. 

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不是种子-每帧都是一样的),并且不能与低精度芯片组一起工作。默认情况下,Gold Noise以低精度运行(快得多)。

Lygia,一个多语言着色器库

如果你不想复制/粘贴函数到你的着色器,你也可以使用lygia,一个多语言着色器库。它在GLSL和HLSL中都包含了一些生成函数,如cnoise, fbm, noised, pnoise, random, snoise。还有很多其他很棒的功能。要做到这一点:

#上的接力包括“文件”,由Khronos GLSL标准定义,并被大多数引擎和环境(如glslViewer, GLSL -canvas VS Code插件,Unity等)所支持。

例如:cnoise

使用cnoise。GLSL与#包括:

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

#include "lygia/generative/cnoise.glsl"

void main (void) {
    vec2 st = gl_FragCoord.xy / u_resolution.xy;
    vec3 color = vec3(cnoise(vec3(st * 5.0, u_time)));

    gl_FragColor = vec4(color, 1.0);
}

为了运行这个例子,我使用了glslViewer。