数学可视化(07)——随机与噪声

  1. 1. 计算机中的随机数
  2. 2. 噪声

 

计算机中的随机数

  • 通过算法生成的随机数一般都是伪随机数
  • 除非我们每次通过不同的随机种子获取

  • 由于shader中标量和向量的元素通常都在0-1区间,因此生成0-1的float值是非常必要的
  • frac:返回指定值的小数部分,返回大于0小于1的值
  • frac函数就是将值映射到0-1区间,可以将sin函数从[-1, 1]映射到[0, 1]

1
half func = uv.y - pow(uv.x, 5);

1
2
half func = uv.y - pow(uv.x, 5);
func = frac(sin(uv.x)*10000);
  • 将这个函数拓展到二维

1
c = frac(sin(dot(uv.xy, float2(12.9898, 78.233)))*43758.05453123);
  • 数字都是随机写的,主要看效果图够不够随机和混沌
  • 还可以替换成rgb3个通道的二维随机

1
2
3
c.r = frac(sin(dot(uv.xy, float2(12.9898, 78.233)))*43758.05453123);
c.g = frac(sin(dot(uv.xy, float2(56.62564, 197.656)))*43758.05453123);
c.b = frac(sin(dot(uv.xy, float2(16.265165, 98.26265)))*43758.05453123);

  • 可以利用floor或cell函数来处理整数索引,使得某块或某段数据获得的随机值是一样的,这样可以做一些tile级别的随机
  • 再利用frac函数处理每一个tile内的uv坐标

1
2
3
4
5
6
uv = uv * 10;
half2 iUV = ceil(uv);
half2 fUV = frac(uv);
c.r = frac(sin(dot(uv.xy, float2(12.9898, 78.233)))*43758.05453123);
c.g = frac(sin(dot(uv.xy, float2(56.62564, 197.656)))*43758.05453123);
c.b = frac(sin(dot(uv.xy, float2(16.265165, 98.26265)))*43758.05453123);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 绘制图形
half3 drawPattern(half2 tile)
{
// 绘制迷宫图案
return smoothstep(tile.x-0.3, tile.x, tile.y)-smoothstep(tile.x, tile.x+0.3, tile.y);
}
// 伪随机函数
float random(in half2 uv)
{
return frac(sin(dot(uv.xy, float2(12.9898, 78.233)))*45363.3264563);
}
// 根据随机值制定uv规则
half2 makeRule(in half2 fUV, in half index)
{
half uv = frac((index-0.5)*2.0);
if (index > 0.75)
{
fUV = half2(1.0, 1.0) - fUV;
}
else if (index > 0.5)
{
fUV = half2(1.0-fUV.x,fUV.y);
}
else if (index > 0.25)
{
fUV = 1.0 - half2(1.0 - fUV.x, fUV.y);
}
return fUV;
}


...
uv = uv * 10;
half2 iUV = ceil(uv);
half2 fUV = frac(uv);
half2 tile = makeRule(fUV, random(iUV));
c = drawPattern(tile);
...
  • 可以修改绘制函数

1
2
3
4
5
6
// 绘制迷宫图案
//return smoothstep(tile.x-0.3, tile.x, tile.y)-smoothstep(tile.x, tile.x+0.3, tile.y);
//绘制圆形图案
return (step(length(tile),0.6) - step(length(tile),0.4) ) + (step(length(tile-float2(1, 1)),0.6) - step(length(tile-float2(1,1)),0.4) );
//绘制三角形图案
//return step(tile.x,tile.y);

噪声

  • 白噪声(White Noise):是值较宽频率范围内,各等宽的频带所含噪声功率谱密度相等的噪声。也就是前面所用的随机值或哈希值产生的噪声,就属于白噪声
  • 值噪声(Value Noise):是由tile四个角上的点的随机值插值生成的噪声图,不过值噪声图只由简单的线性插值生成的话会出现晶格化的现象。前面以tile随机生成的噪声图就是值噪声图
  • 梯度噪声(Gradient Noise):所以会用一些梯度函数进行插值,其中经典的有Perlin噪声、Simplex噪声
  • 网格噪声(Cellular Noise):在程序化生成纹理时更为常用,它是基于距离场的,对于每个像素计算它到最近的特征点的距离,一般来说需要遍历所有四个特征点,计算它们到当前像素点的距离,并把最近的距离保存下来,也就是利用距离场生成了一个Voronoi图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

...
#define time _Time.y
#define w _ScreenParams.x
#define h _ScreenParams.y
// 随机数
float random (in half2 uv)
{
return frac(sin(dot(uv.xy, float2(12.9898,78.233)))*43758.5453123);
}
float2 random2( in half2 uv )
{
return frac(sin(float2(dot(uv.xy, float2(127.1,311.7)),dot(uv.xy,float2(269.5,183.3))))*43758.5453);
}
// 梯度化噪声
float gradientNoise (in half2 uv)
{
half2 iuv = floor(uv);
half2 fuv = frac(uv);
float a = random(iuv);
float b = random(iuv + half2(1.0, 0.0));
float c = random(iuv + half2(0.0, 1.0));
float d = random(iuv + half2(1.0, 1.0));
half2 u = fuv*fuv*(3.0-2.0*fuv);
return lerp(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
// voronoi噪声,计算最小距离
float voronoiNoise(in half2 uv)
{
half2 iuv = floor(uv);
half2 fuv = frac(uv);

float minDist = 1.; //最小距离

for (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// 邻居网格节点
float2 neighbor = float2(float(x),float(y));

// 在网格内从current+neighor位置来随机位置
float2 pt = random2(iuv + neighbor);

// 移动特征点
pt = 0.5 + 0.5*sin(time + 6.2831*pt);

// 像素到特征点的矢量
float2 diff = neighbor + pt - fuv;

// 计算到特征点的距离
float dist = length(diff);

// 计算最小距离
minDist = min(minDist, dist);
}
}
// 返回最小距离
return minDist;
}

...