欢迎继续一起踏上 The Journey of Chaos, 关于 Shader 生成技术的一些基础性学习。噪声会分为以下几篇内容学习
- 随机函数与白噪声
- 值噪声 ValueNoise
- 柏林噪声 Gradient Noise
- 二维柏林噪声 Gradient Noise(本篇)
- 三维柏林噪声与计算优化 Simplex Noise
- Voronoi噪声 Cell Noise
- FBM深入
本来打算柏林噪声一篇学完,但是发现实在是内容太多了... 一维的理解完了之后。 在来一篇学习一下二维。本文会涉及一些高等数学下的知识,主要是空间平面的函数表达式
空间平面
设平面 上一点 和一个法向量 , 则平面的方程为
这个就是平面所谓的 点法式 方程。
然后,将 z 项移到等式的一边,其他项移到另一边:
最后,解出 (z):
因此,点法式平面方程转化为 的形式为:
Geogebra演示
熟悉上面的平面公式之后, 我们假定有两个变量 A ,B. 其构成一个平面公式为, 构建的位置为(0, 0)
然后我们在假定两个变量C D,构建的位置为(1, 0) 起构成的平面公式为
这个时候在对于 a 与 b做一个线性插值 lerp
公式为, 还是用我们之前的招数,先平滑在插值
我们在套用之前的value noise老方法做双线性插值,在(0, 1) (1, 1) 两个点 也搞出这么一个 cd(x,y). 最后我们得到一个漂亮的曲面。
这个蓝色的曲面的 z值,就是 二维 Perlin函数的值。,在后续代码中有一个平面上点到 4 个角的距离与梯度向量做 dot的代码, 这块还很不清楚,我理解应该是类似于获得一个插值。就像在在一座山里面,如果梯度向量是高度变化最快的地方,这个 dot是知道高度具体变了多少,是一个积分的动作?由于数学还不大会
二维柏林
清楚原理之后,代码就变得非常简单。 其结构与 ValueNoise非常相似. 首先是画格子,然后为每一个格子梯度值的生成,非常简单。 通过一个 sin(x) cos(x)即可
vec2 grad(in vec2 id) {
float n = hash21(id);
return vec2(sin(n), cos(n))
}
然后做做梯度与距离的dot
float noise( in vec2 p )
{
vec2 i = floor(x);
vec2 f = fract(x);
vec2 u = ease(f);
vec2 p0 = f - vec2(0.0, 0.0);
vec2 p1 = f - vec2(0.0, 1.0);
vec2 p4 = f - vec2(0.0, 1.0);
vec2 p3 = f - vec2(1.0, 1.0);
float h0 = dot(grad(i+ vec2(0.0, 0.0)), p0);
float h1 = dot(grad(i+ vec2(0.0, 1.0)), p1);
float h4 = dot(grad(i+ vec2(0.0, 1.0)), p4);
float h3 = dot(grad(i+ vec2(1.0, 1.0)), p3);
float m01 = linearInterpolate(h0, h1, u.x);
float m43 = linearInterpolate(h4, h3, u.x);
return linearInterpolate(m01, m43, u.y);
}
三维柏林
三维柏林和二维的区别,就是点的计算不再是vec(0, 0) 而是一个立方体。
这里不在进行重复的讲解了, 我们直接给出 iq大佬 format之后的代码, 很优美。
float noise( in vec3 p )
{
vec3 i = floor( p );
vec3 f = fract( p );
#if INTERPOLANT==1
// quintic interpolant
vec3 u = f*f*f*(f*(f*6.0-15.0)+10.0);
#else
// cubic interpolant
vec3 u = f*f*(3.0-2.0*f);
#endif
return mix( mix( mix( dot( hash( i + vec3(0.0,0.0,0.0) ), f - vec3(0.0,0.0,0.0) ),
dot( hash( i + vec3(1.0,0.0,0.0) ), f - vec3(1.0,0.0,0.0) ), u.x),
mix( dot( hash( i + vec3(0.0,1.0,0.0) ), f - vec3(0.0,1.0,0.0) ),
dot( hash( i + vec3(1.0,1.0,0.0) ), f - vec3(1.0,1.0,0.0) ), u.x), u.y),
mix( mix( dot( hash( i + vec3(0.0,0.0,1.0) ), f - vec3(0.0,0.0,1.0) ),
dot( hash( i + vec3(1.0,0.0,1.0) ), f - vec3(1.0,0.0,1.0) ), u.x),
mix( dot( hash( i + vec3(0.0,1.0,1.0) ), f - vec3(0.0,1.0,1.0) ),
dot( hash( i + vec3(1.0,1.0,1.0) ), f - vec3(1.0,1.0,1.0) ), u.x), u.y), u.z );
}
资料
- 空间平面方程: www.bilibili.com/video/BV1yy…
- adrianb.io/2014/08/09/…
- youtu.be/ZsEnnB2wrbI…
- How to turn a few Numbers into Worlds (Fractal Perlin Noise) www.youtube.com/watch?v=ZsE…
- Simondev noise youtu.be/sChQCdbLdHE…
- ronja www.ronja-tutorials.com/post/026-pe…
- 柏林老哥代码 1980S mrl.cs.nyu.edu/~perlin/doc…
- blog.csdn.net/paserity/ar…