WebGL进阶——走进图形噪声

导语:大自然蕴含着各式各样的纹理,小到细胞菌落分布,大到宇宙星球表面。运用图形噪声,我们可以在3d场景中模拟它们,本文就带大家一起走进万能的图形噪声。

概述

图形噪声,是计算机图形学中一类随机算法,经常用来模拟自然界中的各种纹理材质,如下图的云、山脉等,都是通过噪声算法模拟出来的​。


通过不同的噪声算法,作用在物体纹理和材质细节,我们可以模拟不同类型的材质。

基础噪声算法

一个基础的噪声函数的入参通常是一个点坐标(这个点坐标可以是二维的、三维的,甚至N维),返回值是一个浮点数值:noise(vec2(x,y))
我们将这个浮点值转成灰度颜色,形成噪声图,具体可以通过编写片元着色器程序来绘制。

上图是各类噪声函数在片元着色器中的运行效果,代码如下:

// noise fragment shader
varying vec2 uv;
float noise(vec2 p) {
  // TODO
}
void main() {
    float n = noise(uv);  // 通过噪声函数计算片元坐标对应噪声值
    gl_FragColor = vec4(n, n, n, 1.0);
}

其中noise(st)的入参st是片元坐标,返回的噪声值映射在片元的颜色上。
目前基础噪声算法比较主流的有两类:1. 梯度噪声;2. 细胞噪声;

梯度噪声 (Gradient Noise)

梯度噪声产生的纹理具有连续性,所以经常用来模拟山脉、云朵等具有连续性的物质,该类噪声的典型代表是Perlin Noise。

其它梯度噪声还有Simplex Noise和Wavelet Noise,它们也是由Perlin Noise演变而来。

算法步骤

梯度噪声是通过多个随机梯度相互影响计算得到,通过梯度向量的方向与片元的位置计算噪声值。这里以2d举例,主要分为四步:1. 网格生成;2. 网格随机梯度生成;3. 梯度贡献值计算;4. 平滑插值

第一步,我们将2d平面分成m×n个大小相同的网格,具体数值取决于我们需要生成的纹理密度(下面以4×4作为例子);

#define SCALE 4. // 将平面分为 4 × 4 个正方形网格
float noise(vec2 p) {
  p *= SCALE;
  // TODO
}

第二步,梯度向量生成,这一步是根据第一步生成的网格的顶点来产生随机向量,四个顶点就有四个梯度向量;

我们需要将每个网格对应的随机向量记录下来,确保不同片元在相同网格中获取的随机向量是一致的。

// 输入网格顶点位置,输出随机向量
vec2 random(vec2 p){
	return  -1.0 + 2.0 * fract(
		sin(
			vec2(
				dot(p, vec2(127.1,311.7)),
				dot(p, vec2(269.5,183.3))
			)
		) * 43758.5453
	);
}

如上,借用三角函数sin(θ)的来生成随机值,入参是网格顶点的坐标,返回值是随机向量。

第三步,梯度贡献计算,这一步是通过计算四个梯度向量对当前片元点P的影响,主要先求出点P到四个顶点的距离向量,然后和对应的梯度向量进行点积。

如图,网格内的片元点P的四个顶点距离向量为a1, a2, a3, a4,此时将距离向量与梯度向量g1, g2, g3, g4进行点积运算:c[i] = a[i] · g[i];

第四步,平滑插值,这一步我们对四个贡献值进行线性叠加,使用smoothstep()方法,平滑网格边界,最终得到当前片元的噪声值。具体代码如下:

float noise_perlin (vec2 p) {
    vec2 i = floor(p); // 获取当前网格索引i
    vec2 f = fract(p); // 获取当前片元在网格内的相对位置
    // 计算梯度贡献值
    float a = dot(random(i),f); // 梯度向量与距离向量点积运算
    float b = dot(random(i + vec2(1., 0.)),f - vec2(1., 0.));
    float c = dot(random(i + vec2(0., 1.)),f - vec2(0., 1.));
    float d = dot(random(i + vec2(1., 1.)),f - vec2(1., 1.));
    // 平滑插值
    vec2 u = smoothstep(0.,1.,f);
    // 叠加四个梯度贡献值
    return mix(mix(a,b,u.x),mix(c,d,u.x),u.y);
}

细胞噪声 (Celluar Noise)

Celluar Noise生成的噪声图由很多个“晶胞”组成,每个晶胞向外扩张,晶胞之间相互抑制。这类噪声可以模拟细胞形态、皮革纹理等。

你可能感兴趣的:(WebGL,WebGL,Shader)