数字孪生混乱中生成世界4 多维柏林噪声

103 阅读3分钟

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

  1. 随机函数与白噪声
  2. 值噪声 ValueNoise
  3. 柏林噪声 Gradient Noise
  4. 二维柏林噪声 Gradient Noise(本篇)
  5. 三维柏林噪声与计算优化 Simplex Noise
  6. Voronoi噪声 Cell Noise
  7. FBM深入

本来打算柏林噪声一篇学完,但是发现实在是内容太多了... 一维的理解完了之后。 在来一篇学习一下二维。本文会涉及一些高等数学下的知识,主要是空间平面的函数表达式

空间平面

2501270929 设平面 aa上一点 M0(x0,y0,z0)M_0(x_0, y_0, z_0)和一个法向量 n=(A,B,C)n=(A, B, C), 则平面aa的方程为

A(xx0)+B(yy0)+C(zZ0)=0A(x-x_0) + B(y-y_0) + C(z-Z_0) = 0

这个就是平面所谓的 点法式 方程。

然后,将 z 项移到等式的一边,其他项移到另一边:

Cz=AxBy+Ax0+By0+CZ0Cz = -Ax - By + Ax_0 + By_0 + C*Z_0

最后,解出 (z):

z=ACxBCy+Ax0+By0+CZ0Cz = -\frac{A}{C}x - \frac{B}{C}y + \frac{Ax_0 + By_0 + C*Z_0}{C}

因此,点法式平面方程转化为 z=f(x,y)z = f(x, y) 的形式为:

f(x,y)=ACxBCy+Ax0+By0+CZ0Cf(x, y) = -\frac{A}{C}x - \frac{B}{C}y + \frac{Ax_0 + By_0 + C*Z_0}{C}

Geogebra演示

熟悉上面的平面公式之后, 我们假定有两个变量 A ,B. 其构成一个平面公式为, 构建的位置为(0, 0)

a(x,y)=A(x0)+B(y0)a(x, y) = A(x-0) + B(y-0)

2501272201

然后我们在假定两个变量C D,构建的位置为(1, 0) 起构成的平面公式为

b(x,y)=C(x1)+D(y0)b(x, y) = C(x-1) + D(y-0)

2501273826

这个时候在对于 a 与 b做一个线性插值 lerp 公式为, 还是用我们之前的招数,先平滑在插值

x=3x32x2mix(a,b,x)=a+(ba)xab(x,y)=a(x,y)+(b(x,y)a(x,y))x\begin{aligned} x = 3x^3 - 2x^2 \\ mix(a,b,x) = a + (b - a) * x \\ ab(x, y) = a(x, y) + (b(x,y) - a(x,y)) * x \end{aligned}

2501274556

我们在套用之前的value noise老方法做双线性插值,在(0, 1) (1, 1) 两个点 也搞出这么一个 cd(x,y). 最后我们得到一个漂亮的曲面。

2501275409

这个蓝色的曲面的 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))
}

2501272724 然后做做梯度与距离的dot

2501272818

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) 而是一个立方体。 2501272002 这里不在进行重复的讲解了, 我们直接给出 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 );
}

资料

  1. 空间平面方程: www.bilibili.com/video/BV1yy…
  2. adrianb.io/2014/08/09/…
  3. youtu.be/ZsEnnB2wrbI…
  4. How to turn a few Numbers into Worlds (Fractal Perlin Noise) www.youtube.com/watch?v=ZsE…
  5. Simondev noise youtu.be/sChQCdbLdHE…
  6. ronja www.ronja-tutorials.com/post/026-pe…
  7. 柏林老哥代码 1980S mrl.cs.nyu.edu/~perlin/doc…
  8. blog.csdn.net/paserity/ar…