数字孪生混乱中生成世界3 梯度噪声 Perin Noise

89 阅读4分钟

欢迎继续一起踏上 The Journey of Chaos, 关于 Shader 生成技术的一些基础性学习。噪声会分为以下几篇内容学习

  1. 随机函数与白噪声

  2. 值噪声 ValueNoise

  3. 柏林噪声 Gradient Noise(本篇)

  4. 二维柏林噪声 Gradient Noise

  5. 三维柏林噪声与计算优化 Simplex Noise

  6. Voronoi噪声 Cell Noise

  7. FBM深入

本篇主要是理解 柏林噪声的具体实现原理,相关的学习资料和衍生内容在文尾。

Perlin 噪声

为什么柏林噪声相对于值噪声表现更好。 个人感觉有点类似于之前学过的 Iphone的 G2连续 。 一个曲线或曲面可以被描述为具有 Gn 连续性,n 是表示光滑度的增量,即在曲线上取一点,然后分析该点与其两侧线段的关系。

  • G0: 两侧曲线在这一点相遇(位置连续)。

  • G1: 两侧曲线这一点的切线方向相同(相切连续)。

  • G2: 两侧曲线在这一点的曲率相同(曲率连续)。

Gradient Noise就是 G2, Value Noise就是 G1...... 当然大概率是我学串了...

回到我们的主题 柏林噪声, 它是由Ken Perlin在20世纪80年代发明的一种伪随机梯度噪声。主要用于生成自然的纹理效果,如云彩、火焰、海浪等,因为生成的噪声具有连续的梯度变化,显得更加平滑和自然。柏林噪声通过插值方法计算,输入可以是多维的,如一维、二维、三维等,从而生成不同维度下的噪声。

本章会接触到一个叫做梯度 Gradinent的数学概念,不过不要害怕,对于柏林噪声来说他只是一个值而已。 先记住一个概念,梯度就是值变化最快的方向。 例如在一维的柏林噪声中,一维梯度就是曲线的导数, 二维的梯度可以想象我们在一个山坡上,我们朝那个方向迈出步子,让我们所在位置高度变化最快,这个方向就是二维的梯度。 在高维的梯度就纯粹是抽象的向量与偏导数概念。

为了更好的了解,我们从一维柏林开始

一维 Perlin函数

2501265738

2501265738

考虑上面 A,B,C三个点,我们为每个点选择一个斜线。还记得高中里面这条线是该点的斜率。 所以三条线的公式应该有

\begin{aligned}
a = n0 \dot (x - 0) \
b = n1 \dot (x - 1) \
c = n2 \dot (x - 2)
\end{aligned}
\

这里的 n0, n1, n2就是斜率,也是导数值,👀 一维的导数就是梯度值。

这个时候我们再利用上一节课讲到的插值函数Interpolate和平滑函数 Ease. 插值我们依然选择线性插值

interploate(x, a, b) = a + (b-a) \times x
\

缓和函数我们依然选择 Smoothstep

ease(x) = 3x^3 - 2x^2\

套用到上面公式中,可得

\begin{aligned}
final(x) &= a + (b-a) * ease(x) \
& = a + b * ease(x) - a * ease(x) \
&= n0 \dot (x - 0)  + n1 \dot (x - 1) * ease(x) - n0 \dot (x - 0) * ease(x)
\end{aligned}
\

这个函数的曲线就是如下蓝色所示。 That's All 这就是一维的 Perlin噪声。 和我们之前 G2连续好像...

2501263633

2501263633

一维Perlin代码

理解起数学公式之后,写代码就是一个很简单的事情。 首先实现我们的插值函数

float linearInterpolate(float a , float b,float x) {    return a + (b -a ) * x;}

在实现 ease函数

float ease(float x) {    return x * x * (3.0 - 2.0 * x);}

套用公式实现就是先 perlin噪声

float perlinNoise(float x) {    float i = floor(x);    float f = fract(x);            float d0 = hash11(i+0.0);    float d1 = hash11(i+1.0);    return linearInterpolate(d0, d1, ease(f));}

这个时候我们在将 perlinNoise产生的值作为 y值,画一个线,就可以看到一个噪声曲线啦

    float y = 0.5 + 0.5 * perlinNoise(uv.x);    vec3 col = vec3(1.0 - smoothstep( 0.0, 0.01, abs(p.y-y) ));

2501262448

2501262448

其实拿到上面那根线进行简单的加工就可以很有意思。。。 比如重复几条线,然后给线加点颜色

       const float lineNum = 8.;    for (float i =0.; i< lineNum; i++) {        float rand = perlinNoise(uv.x * 1.2 * i / lineNum + iTime) * 0.5  + 0.25;        float halfWidth = mix(0.015, 0.15, rand * horizontalFade) / 2.0;        col += smoothstep( halfWidth/3., 0.0, abs(p.y-rand) ) * palette(rand * 3.0);        col += smoothstep(halfWidth/10. + 0.015,halfWidth/10., abs(p.y-rand) )* palette(rand * 3. + 23.) * 0.2;    }    

可以得到下面很有意思的图像 2501260118

资料

  1. adrianb.io/2014/08/09/…

  2. youtu.be/ZsEnnB2wrbI…

  3. How to turn a few Numbers into Worlds (Fractal Perlin Noise) www.youtube.com/watch?v=ZsE…

  4. Simondev noise youtu.be/sChQCdbLdHE…

  5. ronja www.ronja-tutorials.com/post/026-pe…

  6. 柏林老哥代码 1980S mrl.cs.nyu.edu/~perlin/doc…

  7. blog.csdn.net/paserity/ar…