GLSL的随机/噪声函数

阅读次数 135

最近迷恋上了分形之类的艺术效果,大多会使用噪声函数,这些噪声函数可以用来生成自然、有机的图案和纹理,而分形则是通过将噪声函数以不同的频率和振幅组合起来,来创建具有自相似性(self-similarity)的复杂结构。而噪声函数我看有些地方的实现都不是太一样,有没有比较统一的标准来定义一些随机或噪声函数?

2 Answers

在计算机图形学中,随机数和噪声函数是创建自然、有机图案和纹理的重要工具。在GLSL(OpenGL Shading Language)中,实现随机数和噪声函数的方法有很多种,但确实存在一些常用的模式和技巧。

随机函数

随机函数通常用于生成特定范围内的随机数。在GLSL中,你可以使用内置函数如fract(), sin(), mix()等来创建简单的随机函数。

float random(float seed) {
    return fract(sin(seed) * 43758.5453123);
}

这个函数使用了Sine函数来生成一个看起来随机的值。你可以通过改变seed参数来获得不同的随机结果。

噪声函数

噪声函数比随机函数更复杂,它能产生更加平滑、连续的结果。其中最常用的是Perlin噪声和Simplex噪声。

Perlin噪声

Perlin噪声是由Ken Perlin发明的一种噪声函数,它产生的噪声具有很好的平滑性和方向性。在GLSL中实现Perlin噪声稍微复杂一些,需要考虑梯度向量、插值等。

这里是一个简化版的Perlin噪声实现:

vec3 mod289(vec3 x) {
    return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 mod289(vec4 x) {
    return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 permute(vec4 x) {
    return mod289(((x*34.0)+1.0)*x);
}

float snoise(vec3 v) {
    const vec2 C = vec2(1.0/6.0, 1.0/3.0);
    const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);

    // First corner
    vec3 i_ = floor(v + dot(v, C.yyy));
    vec3 x0 = v - i_ + dot(i_, C.xxx);

    // Other corners
    vec3 g = step(x0.yzx, x0.xyz);
    vec3 l = 1.0 - g;
    vec3 i = min(g.xyz, l.zxy);
    vec3 j = max(g.xyz, l.zxy);

    // Positions and indices
    vec3 x1 = x0 - i + C.xxx;
    vec3 x2 = x0 - j + C.yyy;
    vec3 x3 = x0 - 0.5;

    // Permutation
    i = mod289(i);
    j = mod289(j);
    vec4 i4 = permute(permute(permute(i.z + vec4(0.0, i.y, i.x, 1.0)) + j.z + vec4(0.0, j.y, j.x, 1.0)));

    // Gradients
    vec2 p0_ = vec2(x1.x, x2.x);
    vec2 p1_ = vec2(x1.yz, x2.yz);
    vec2 p2_ = vec2(x2.xy, x3.xy);
    vec2 p3_ = vec2(x2.zw, x3.zw);

    vec4 norm = taylorInvSqrt(vec4(dot(p0_, p0_), dot(p1_, p1_), dot(p2_, p2_), dot(p3_, p3_)));
    p0_ *= norm.x;
    p1_ *= norm.y;
    p2_ *= norm.z;
    p3_ *= norm.w;

    // Mix final noise value
    vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
    m = m * m;
    return 42.0 * dot(m*m, vec4(dot(p0_, x0), dot(p1_, x1), dot(p2_, x2), dot(p3_, x3)));
}

这个函数实现了一个简化的Perlin噪声算法。注意,Perlin噪声的完整实现会更复杂,包括梯度向量的选择和插值方法的细节。

分形噪声

分形噪声(如Fractal Brownian Motion, FBM)是通过将多个不同频率和振幅的噪声叠加在一起形成的。这种技术可以创造出具有自相似性的复杂结构。

float fbm(vec3 uv, float octaves, float persistence) {
    float total = 0.0;
    float amplitude = 1.0;
    float frequency = 0.0;
    for (int i = 0; i < int(octaves); i++) {
        total += snoise(uv * frequency) * amplitude;
        amplitude *= persistence;
        frequency += 1.0;
    }
    return total;
}

在这个例子中,fbm函数使用了octaves参数来控制噪声层数,persistence参数来控制每层的振幅衰减率。

这些基本的噪声函数和分形噪声函数可以被进一步扩展和优化,以适应具体的应用场景。