perlin噪声_Perlin噪声与Simplex噪声笔记

本文介绍了Perlin噪声和Simplex噪声的概念和原理。Perlin噪声基于梯度噪声,而Simplex噪声是对Perlin噪声的优化,具有更低的计算复杂度O(n^2)。在高维场景下,Simplex噪声表现出更好的性能,并且没有明显的方向性,适合用于纹理生成、模拟等领域。

8aa547557bd8362ccb9186d8cb6c323a.png

首先这两种噪声都是基于晶格的方法(Lattice based),Simplex噪声是对Perlin噪声的一种改进,基本思想是一样的。

Perlin噪声

Perlin噪声属于梯度噪声,其原理是简单来说就是将坐标系划分成一块一块的晶格,之后在晶格的顶点处生成一个随机的梯度,通过与晶格顶点到晶格内点的向量进行点乘加权计算后得到噪声。

4694b339e98548c71c3542977106a89c.png
图1 二维Perlin噪声示意图

具体的三个步骤:

  1. 定义晶格结构,例如在shader中通过float2 p = i.uv * n可以得到 n x n 的结构,图2展示的是二位的晶格结构,三维的话则是立方体网格。得到晶格后通过floor操作获取晶格id,之后便可得到晶格四个顶点的信息,通过伪随机设定每个顶点的梯度向量(其实就是一个随机的二维向量),梯度向量的值需要最终需要处[-1, 1]之间。

cacbdf766627a1956676d339b7523f51.png
图2 10 x 10 结构

2. 在fs中通过插值后的的uv信息获取当前p点,通过frac操作获取相对于晶格id的偏移值。之后计算其所在晶格的顶点到p点的距离向量。之后分别与晶格顶点的梯度向量点乘

3. 通过插值计算权重。其中插值使用的缓和曲线(ease curves)有两种:最初的Perlin噪声中使用的是:

5bc0140bbc9334bc614b3680dac948bd.png
图3 原缓和曲线

之后提出了新的缓和曲线:

cb14158cf51852714a0c6e0023b2623a.png
图4 二阶导为0的缓和曲线

相比于原来的曲线,新的缓和曲线二阶导和一阶导在0与1处都为0,得到的噪声在晶格相互交界处更加平滑。

代码:

            float2 perlin_random(float2 p)
            {
                float2 noNormalize = -1.0 + 2.0 * frac(
                    sin(
                            float2(dot(p, float2(127.1, 311.7)),
                                   dot(p, float2(269.5, 183.3))
                                   )
                        ) * 43758.5453
                                                      );
                return noNormalize;
            }
            //通过随机取晶格四个点的梯度,然后与点乘计算四个点p点的贡献程度
            //复杂度O(n^2)
            float perlin_noise(float2 p, float random1 = 0, float random2 = 0, float random3 = 0)
            {
                float2 pi = floor(p);//获取当前网格索引i
                float2 pf = frac(p);//获取当前片元在网格内的相对位置
                //计算gradient梯度贡献值
                float2 g00 = perlin_random(pi);
                float2 g10 = perlin_random(pi + float2(1.0, 0.0));
                float2 g01 = perlin_random(pi + float2(0.0, 1.0));
                float2 g11 = perlin_random(pi + float2(1.0, 1.0));
                //求得点乘
                float v00 = dot(g00, pf);
                float v10 = dot(g10, pf - float2(1.0, 0.0));
                float v01 = dot(g01, pf - float2(0.0, 1.0));
                float v11 = dot(g11, pf - float2(1.0, 1.0));
                //平滑插值
                // float2 lerp_xy = smoothstep(0.0, 1.0, f);
                float2 lerp_xy = pf * pf * (3.0 - 2.0 * pf);
                //梯度贡献值插值
                float2 n_x = lerp(float2(v00, v01), float2(v10, v11), lerp_xy.x);
                float n_xy = lerp(n_x.x, n_x.y, lerp_xy.y);
                return n_xy;
            }

//调用
float col = perlin_noise(i.uv + float2(_xOffset, _yOffset)) * _scale * 0.5  + 0.5;

Simplex噪声

Perlin噪声还是比较好理解的,这个Simplex就很绕人了,虽然基本原理是一样的,都是采用晶格点生成梯度,通过梯度向量与晶格点到p点的向量进行点乘后加权计算得出噪声。不同的地方是,相比于Perlin噪声的O(2^n)的复杂度,Simplex噪声的复杂度是O(n^2),在高纬度上有着更好的优化。但是与Perlin噪声不同的是,其采用的不是晶格,而是单形。在2D情况下,Perlin的晶格以正方形为单位,Simplex的晶格以正三角形为单位。在三维情况下,Perlin的晶格以立方体为单位,Simplex的晶格以四面体为单位。

9eb1a3549ec0cbfd4e83aa5ac51adf10.png

06de738aa7a273c8b20eeb0d8c62a796.png
图5 二维,三维情况下的Simplex晶格

如果你只关注算法思想的话还是很好理解的:找到p点所在的单形晶格,计算晶格顶点梯度向量与顶点到p点的向量进行点乘后加权。困难的地方在于如何求得晶格id与晶格点到p点的向量。

解决方法是将晶格偏斜(skewing)后得到正立方体构成的网格结构。如图6所示

03f032efaf30b4a0b2f8cbeef3669c28.png
图6 将网格偏斜

其中的偏斜公式:

184dc5d41de65d10ef095f28d85852e9.png
图7 单形网格坐标系下偏斜计算公式

925536647c9573ff41b143a1f54fbe10.png
图8 正超晶体坐标系下求得偏斜前的位置的计算公式

这里一开始把我绕进去了,通俗来说,图7的公式是给单形网格坐标系下面用的,(x, y)对应偏斜前的坐标,(x', y')对应偏斜后的坐标。图8的公式是给正超晶体坐标系用的(即图6中的正正方方的坐标系),(x, y)为偏斜后的位置,(x', y')为偏斜前的位置,这两个公式相当于两个坐标系下的正反操作,求得噪声的计算公式为

4c13f645ecb863a50bb47696dae5aa7d.png
图9 Simplex noise公式

e5646e93122b3e8ef8e668d11a6eb08b.png
图10 Simplex noise公式

以二维噪声为例,

float simplex_noise(float2 p)
{
    const float F = 0.366025404; // (sqrt(3)-1)/2;
    const float G = 0.211324865; // (3-sqrt(3))/6;

    float2 i = floor(p + (p.x + p.y) * F);//P点变形后成超立方体后的立方体所在的索引号

    float2 a = p - (i - (i.x + i.y) * G);//变形前输入点到(0, 0)点的距离向量
    float2 o = (a.x < a.y) ? float2(0.0, 1.0) : float2(1.0, 0.0);
    float2 b = a - o + G;//变形前输入点到(1, 0)点或(0, 1)点的距离向量
    float2 c = a - 1.0 + 2.0 * G;//变形前输入点到(1, 1)点的距离向量

    float3 h = max(0.5 - float3(dot(a, a), dot(b, b), dot(c, c)), 0.0);
    float3 n = h * h * h * h * float3(dot(a, hash22(i)), dot(b, hash22(i + o)), dot(c, hash22(i + 1.0)));

    return dot(float3 (70.0, 70.0, 70.0), n);
}

其中输入点p是变形前的顶点坐标,即在单形坐标系内的坐标,通过(i - (i.x + i.y) * G)可以确定变形前(即单形坐标系下)p点所在晶格的A点的坐标。a是在变形前p点到A点的距离向量。o点则需要通过p.x与p.y判断所在晶格,因为偏斜后的立方体晶格是由2个单形晶格组成的(图6所示),通过o可以求得B点,B点为左上或者右下点。之后C点即晶格的右上角点。由此可以得到变形前(单形坐标系下)三个晶格顶点与P点,便可以进行点乘计算了。这里还有一个0.5需要说明,如果超立方体坐标系下每个晶格长度为1,则可以计算出单形晶格中每个三角形的边长的平方为2/3,由此可以求出正三角形的高的平方为1/2,即0.5,至于为什么这里是r²,因为Simpex的计算使用径向衰减函数对每个顶点的贡献值进行计算然后求和

43218e435241b3c61263fe2188b09290.png
图11 Simplex noise的径向衰减

其中,dist是输入点到该顶点的距离向量,grad是该点存储的伪随机梯度向量,r^2的取值是0.5或0.6。取0.5时可以保证没有不连续的间断点,取0.6得到更好的视觉效果。0.6也是Ken Perlin在算法中引用的值。求和后我们通常还会将结果归一化到[-1,1]空间中去。因为需要归一化,所以return的时候使用了70.0与结果进行点乘。这个值怎么来的?

    float3 h = max(0.5 - float3(dot(a, a), dot(b, b), dot(c, c)), 0.0);

在这一步中若想得到最大值,P点必须位于三角形某一边的中点,得到的值为

h = float3(0, 1/3, 1/3);

h = float3(1/3, 0, 1/3);

h= float3(1/3, 1/3, 0);

取梯度时的梯度范围为[-1, 1],因此梯度最大值的平方为2。因此

fabe6fec2a47c000df7a9bdc83e32802.png
图12 二维Simplex,半径取0.5时的伸缩值

Simpex噪声的优点

Perlin噪声的算法复杂度为O(2^n),生成噪声维度越高,计算复杂性增长越快,同时,在实现时对Permutation表的操作也越复杂,插值运算也越多,代码操作时难度也越来越大。

1、Simplex 噪声生成时复杂度得到降低,乘法运算减少。

2、Simplex 噪声生成算法复杂度为O(n^2),较原始Perlin 噪声 O(2^n)的计算复杂度有所降低,特别在高维度时表现更为出色。

3、Simplex 噪声因其无缝重复性可以在大范围内随意扩展。

4、Simplex 噪声没有明显方向性的人工痕迹(各向同性),虽然各方向生成的噪声看起来不一样。

5、Simplex 噪声有定义良好处处连续的计算更简单的梯度值。

6、Simplex 噪声有良好的硬件亲和性。

Simplex噪声复杂度的降低来源于对晶格的选取,因单形固有的性质可以使计算的梯度值大大减少从而大幅降低了计算量。

参考:

Simplex Noise(一)​blog.csdn.net
05992dd1a3995e7a6294c92dadff87d8.png
【图形学】谈谈噪声_candycat-CSDN博客​blog.csdn.net
c3922936d01f12f289b7abcd45e0c08b.png
http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf​staffwww.itn.liu.se
Real-Time Shading SIGGRAPH Course Notes, Perlin K. Noise hardware[J].,2001.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值