如何利用噪声生成纹理进行可视化学习?
摘要:什么是噪声呢?在自然界中,离散的随机是比较常见的,比如蝉鸣突然响起又突然停下,比如雨滴随机落在一个位置,但是随机和连续并存是更常见的情况,比如山脉的走向是随机的,但山峰之间的高度又是连续的,比如天上的云朵、水面的波纹等等。这种把随机和连续结
本文分享的是如何使用噪声生成纹理。
首先,什么是噪声呢?在上篇文章中我介绍过一个生成随机数的函数,利用随机技巧我们生成了一个类似剪纸的图案,那在自然界中,这种离散的随机也是比较常见的,比如蝉鸣突然响起又突然停下,比如雨滴随机落在一个位置,但是随机和连续并存是更常见的情况,比如山脉的走向是随机的,但山峰之间的高度又是连续的,比如天上的云朵、水面的波纹等等。
那么这种把随机和连续结合起来,就形成了噪声。
通过利用噪声,我们就可以去模拟真实自然的图案。
接下来就介绍几种生成噪声的常用算法。
插值噪声
首先是比较容易理解的插值噪声,Value noise。
一维噪声
我们先来看一个小例子。
// 随机函数
float random (float x) {
return fract(sin(x * 1243758.5453123));
}
void main() {
vec2 st = vUv - vec2(0.5);
st *= 10.0;
float i = floor(st.x);
float f = fract(st.x);
// d直接等于随机函数返回值,这样d不连续
float d = random(i); // 取出10个不同的'd'值(0~1)
// st.y: -5 ~ +5
// 1. d < st.y - 0.1 或 d > st.y + 0.1,值为0,为黑色(st.y > d+0.1 或 st.y < d-0.1)
// 2. st.y - 0.1 < d < st.y + 0.1 时, 值为0->1->0,为黑到白再到黑的过渡色
gl_FragColor.rgb = (smoothstep(st.y - 0.10, st.y, d) - smoothstep(st.y, st.y + 0.10, d)) * vec3(1.0);
gl_FragColor.a = 1.0;
}
通过这段代码我们在画布上绘制了10条线段,那么这10条线段是怎么生成的呢?
我们来看代码,首先通过减去vec2(0.5),相当于把纹理坐标轴的原点挪到了(0.5, 0,5)的位置,然后乘以10,就是把 st 坐标值放大10倍,得到一个10 x 10的网格,这里也有一个生成伪随机数的函数,可以看出是根据片元所在的网格、在X轴方向的索引,生成了一个随机数,所以也就是说整个画布的片元去运算,会得到10个不同的 d 值。
然后我们看这个生成伪随机数的函数,它的返回值的范围,其实是在0到1之间,也就是说后面的d,它是一个0到1之间的值。
最后我们看这个色值的计算,当 st.y > d+0.1 或者 st.y < d-0.1时,这个值是0,也就为黑色,而st.y的范围本来就在-5到5之间,所以我们可以看到这个随机计算出来的10条线段是靠近X轴的。
在上面的代码中,我们生成的是10条离散的线段,如果我们想将计算出来的离散的值连起来,我们可以使用mix函数。
// mix(a, b, c):线性插值函数。a和b是两个输入的颜色或值,c是一个介于0和1之间的浮点数,表示插值的权重
// 当c接近0时,返回a;当c接近1时,mix函数返回b;当c在0和1之间时,返回a和b的插值结果。
float d = mix(random(i), random(i + 1.0), f);
这样我们就会得到一段连续的折线。f 是取st的小数部分,是片元在它自身所在的网格内的X坐标,它的范围是在0到1之间。
我们看到折线虽然是连续的,但它看上去并不够自然,因此我们可以改用smoothstep或者三次多项式f*f*(3.0-2.0*f),这样就能得到一条连续且平滑的曲线。
float d = mix(random(i), random(i + 1.0), smoothstep(0.0, 1.0, f));
// float d = mix(random(i), random(i + 1.0), f * f * (3.0 - 2.0 * f));
随机加连续,所以这就是噪声函数了。
二维噪声
可以看到,这个噪声的生成方式,是在首尾两个点之间进行插值。用了一个坐标去生成一个随机值,这是一维噪声。如果要生成二维的图案,我们需要使用二维噪声,需要对平面画布上 方形区域 的四个顶点,分别从x、y方向进行两次插值。
