最近迷恋上了分形之类的艺术效果,大多会使用噪声函数,这些噪声函数可以用来生成自然、有机的图案和纹理,而分形则是通过将噪声函数以不同的频率和振幅组合起来,来创建具有自相似性(self-similarity)的复杂结构。而噪声函数我看有些地方的实现都不是太一样,有没有比较统一的标准来定义一些随机或噪声函数?
最近迷恋上了分形之类的艺术效果,大多会使用噪声函数,这些噪声函数可以用来生成自然、有机的图案和纹理,而分形则是通过将噪声函数以不同的频率和振幅组合起来,来创建具有自相似性(self-similarity)的复杂结构。而噪声函数我看有些地方的实现都不是太一样,有没有比较统一的标准来定义一些随机或噪声函数?
在计算机图形学中,随机数和噪声函数是创建自然、有机图案和纹理的重要工具。在GLSL(OpenGL Shading Language)中,实现随机数和噪声函数的方法有很多种,但确实存在一些常用的模式和技巧。
随机函数通常用于生成特定范围内的随机数。在GLSL中,你可以使用内置函数如fract()
, sin()
, mix()
等来创建简单的随机函数。
float random(float seed) {
return fract(sin(seed) * 43758.5453123);
}
这个函数使用了Sine函数来生成一个看起来随机的值。你可以通过改变seed
参数来获得不同的随机结果。
噪声函数比随机函数更复杂,它能产生更加平滑、连续的结果。其中最常用的是Perlin噪声和Simplex噪声。
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
参数来控制每层的振幅衰减率。
这些基本的噪声函数和分形噪声函数可以被进一步扩展和优化,以适应具体的应用场景。