https://www.bilibili.com/video/BV1bb4y1a7Bu/?spm_id_from=333.337.search-card.all.click&vd_source=01ae3fc576acbf5bd3e59f3307bb594f
背景
其实早在80年代之前,随机数是基本能满足我们日常需求的,但是随着人们应用的越加广泛,其问题也也越发明显。我们接下来我们首先介绍下random函数和noise函数的区别。
random(a) | noise(a) | |
---|---|---|
参数含义 | 随机值范围 | 挖矿点坐标 |
随机值范围 | 传入值参数指定 | 0~1 |
随机值分部 | 均匀分部 | 类似正态分部 |
其他 | 几乎每次输出的值都不一样 | 相同位置挖出的值一样 |
简单解释下这四部分内容:
(1)参数含义意义:random传入的值代表随机值范围,也就是返回的值所在的范围区间,这块大家可以去自行举例法测试,或者大家在其他语言中一定也有random函数,其参数含义也大致如此。noise函数参数代表传入的值,我们称为“挖矿点”,通过传入变量值,最终返回相应的结果值。
(2)随机值范围意义:random由传入的参数值确定,而noise在0~1之间。如下图所示:
(3)随机值分部意义:random返回的值是均匀分部的,noise返回的值是近似于正态分部。
(4)其他意义:random输出的值每次都不一样,noise如果传入的值是固定的,其返回的结果也是相同的。这里我们可以利用初级课程中学到的通过颜色值调试glsl代码去做详细的输出打印,伪代码如下所示:
for(int i=0;i<10;i++){
float a = random(100);
}
for(int i=0;i<10;i++){
float a = noise(100);
}
Ken Perlin(这哥们为了解决在电影中特效问题,发明了一个算法,也就是因为这个算法获得了奥斯卡金奖)发明的自然噪声生成算法。接下来我们详细介绍下该算法的应用情况。
1D噪声
我们是在前面给大家讲了关于随机数的算法,其大致图像如下图所示:
Perlin Noise的图像大致如下所示,但是这个图像我最开始在背景里面给大家讲解过,只要传入的值是固定的,返回的值也是固定的,这就是不同与随机数的区别。
接下来解释下噪声函数生产的核心代码:
float i = floor(x); // 整数
float f = fract(x); // 小数
float u = f * f * (3.0 - 2.0 * f );
y = mix(rand(i), rand(i + 1.0), u);
i
和f
均是为了将x上的像素值分割成整数和小数。函数float u = f * f * (3.0 - 2.0 * f );
实际上一个三次多项式函数,近似于smoothstep
函数,目的是为了将值进行平滑过渡,mix
函数是rand(i)与 rand(i + 1.0)
进行插值。其函数图像如下所示:
***注:***这里面我觉得巧妙之处就是将x进行分割,增加随机性,最后实际上还是应用x,表达的y之间的关系。实现x与y一对一对应关系。
2D噪音
上述我们介绍了1维噪音生产计划,noise(x),下面我们介绍二维噪音生产计划noise(x,y),任意输入一个x,y即可得到相应的noise值,我们现在看到的就是将x,y值返回的noise映射成黑白灰的样子:
如果将x,y对应的值看成height,就会有下面的效果:
同样的,2Dnoise也可以像1Dnoise一样在平面上无限延伸,我们实际上在游戏里生成的地形,就是这样生成的,由此看来2D的noise函数和1D的noise函数本质上没有区别,只不过多了一个维度。下面是2Dnoise的不同表现形式:
如果表现灰度就是第一个,表现颜色就是第一行第二列,表现速度方向就是第二行第一列,表现高度就是最后一个。
3D噪音
noise 在3D中的应用也是类似的,通过输入三个变量值noise(x,y,z),如下图所示:
假如在某一位置,x,y固定,即通过x,y确定了某一平面,同时随着z轴变化,x,y所形成的投影不断变化;同样道理,x,z的值是固定的,随着y值的变化,x,z行成的平面不断变化,最后也生成了平面图例。
FBM
有时候单一频率的噪声不足以满足需求,会需要使用多级噪声累加的结果来实现程序化生成,这种方式我们称之为Fractal Brownian Motion
,简称FBM
,下图展示了FBM
的基本形式,简单来说就是将多个不同频率的噪声按照不同的振幅进行混合:
float fBm(float x)
{
// Properties
int octaves = 1;//int(iMouse.x * 0.01);
float lacunarity = 2;
float gain = 0.5;
// Initial values
float amplitude = 1;
float frequency = 1;
float value = 0;
// Loop of octaves
for (int i = 0; i < octaves; ++i)
{
value += amplitude * noise(frequency * x);
frequency *= lacunarity;
amplitude *= gain;
}
return value;
}
FBM
的关键在于多个不同频率的noise
的叠加到一起,其中for
循环是用来产生多个noise
,至于不同频率的noise
一般可以关注以下这个公式:
f
l
o
a
t
r
e
s
=
a
∗
n
o
i
s
e
(
b
∗
x
)
;
float res= a * noise(b * x);
floatres=a∗noise(b∗x);
其中a和b两个参数是关键,我们可以尝试动态改变一下这个参数。
总结
noise不管是1D、2D、3D,可以比喻成在茫茫大地上存储的矿石,无穷无尽。通过noise函数输入的索引,可以获取任意位置的金矿,索引坐标可以是1D、2D、3D。可以把其中一个维度理解为时间。
(1)Noise 使用方法: 用开采出来的数值控制各种属性。
(2)如果你想将noise生成的值赋值给不同的属性,一定要错开不同的 Noise 取值区间,来避免变化趋势相同,一般我们是在noise输入参数上乘上某值,让输入的参数范围相距更远。
具体我们来看一下下面这个案例。