unity c 语言教程,程序丨Unity教程:如何用最简单的方式创建Simplex噪声?

原标题:程序丨Unity教程:如何用最简单的方式创建Simplex噪声?

翻译:刘甜甜(青悠)

审校:周伟杰 (Senser)

在本篇教程中,你将学会创建Value噪声与Perlin噪声的替代噪声,即名为Simplex 噪声。你需要先了解以下内容:

• 使用基于距离的衰减函数

• 正方形网格与三角形网格的转换

• 立方体网格与四面体网格的转换

• 拓展到3维,使用导数计算Simplex Value噪声

• 拓展到3维,使用导数计算Simplex Gradient噪声

本教程与噪声和噪声导数教程是一脉相承的。我假设你首先已经学习过了前面的内容,因为我们将会使用到前面所讲内容中的代码以及其中的知识。

这篇教程使用的是Unity 4.5.2.,并且在此之前的老版本可能不会奏效。

d015a5fa681bbafecf33395ed4b67298.png

粒子环绕Simplex 旋度噪声

简化噪声

在噪声以及噪声导数教程中,我们使用伪随机噪声来为纹理着色,使得能够实现平面变形以及动画粒子流。我们我们创建了两种形式的晶格杂讯,在网格线的交点进行插值。我们选择使用了一个超立方体网格——在1 维中为网格线,在2 维中为正方形网格,以及在3维中为立方体网格——因为这是一个简单明了的划分空间的方式。

我们通过定义每个晶格点的散列值和格点之间的平滑插值来创建Value噪声。且我们通过梯度插值而不是用固定值来创建Gradient噪声,这也就是通常被大家称之为的Perlin噪声。

你可以利用这些噪声类型生成很好的效果,但是它们也存在一些局限性。由于这些噪声在创建时基于超立方体网格,故而如果你能够细心观察,你就可以探测到其中的正方形模式。同样的,当通过一个3维噪声移动一个轴对称的2维切片,且如果从立方单元的边和中心点切换时,就会发现存在明显的变化效果。由于三次插值,立方体的中心与其表面相比将会更加的失真。最终,用解析求导法来计算是非常困难的,并且在更高维度中计算成本会更加昂贵。4 维噪声就需要一个超立方体的网格,这意味着你必须得为每个样本插值16个分点。

使用Simplex网格

但我们不需要使用一个超立方体网格,我们所需要做的所有事情就是将一个空间分割成固定的区块。在理想情况下,我们会尽可能使用最小形状的网格单元,这样做才会生成一个最简simplex噪声。对于1维来说,这就是一个线条,所以它没有任何区别可言。对于2维来说,这会是一个三角形而不是一个正方形。对于3 维来说,这会是一个四面体而不是一个立方体。并且对4 维而言,这是一个正五胞体而不是超正方体。这意味着,对于n维度来说,我们只考虑1 + n个点而不是2 的次方个点。

用衰减函数代替插值

我们如何在simplex的各拐角处实现插值呢?实际上,我们并不需要进行插值。我们可以依据拐角到样本目标点的距离来减少拐角的加成,从而代替插值计算。对于2 维而言,这将形成圆形衰减,在到达三角形的对边时它将会降至0。对于3 维而言,这将形成球形衰减,等等以此类推。

使用衰减函数而不是插值的巨大优势在于,这会使得每个点的加成能够独立计算。它们叠加后就会得到最终值。这就在很大程度上简化了导数计算的过程。

你可以在超立方体网格中使用同样的计算方法, 但在方形上的圆形衰减并没有三角形上的圆形衰减那么奏效。

f6be3853b2a8c4b2b011ee8034a8718a.png

2维的Value噪声与Perlin噪声,辐射状衰减对比插值的效果

Simplex噪声

Simplex噪声没有申请专利吗?

使用simplex网格以及衰减函数去计算梯度噪声是 Ken Perlin首次提出来的,他用这种方式代替了他先前发明的梯度噪声计算方法。由于大多数人将此称为simplex噪声,故而我们也就把基于simplex的梯度噪声沿用了相同的命名方式。对于我们的Value噪声,我们就命名其为Simplex Value噪声,把它和基于超立方体的Value噪声区分开来。

我们将建立在前面教程的基础上,从噪声导数教程的 finished project开始入手。

SimplexValue噪声

让我们继续从Value噪声开始探讨,因为Value噪声要比Gradient噪声来得简单。然而,在我们继续探讨之前,我们要记得到目前为止我们都将Value噪声当做了一个特例的噪声来对待。这是因为Value噪声的范围是0 到1,而Gradient噪声的范围为-1 -到1。所以我们也应该更新我们的代码,把Simplex Value噪声也作为一个特例。或者是,如果我们可以改变的Value噪声的范围为-1 -到1的话,我们就完全可以清除这种特殊的状况。我们选择改变它的范围是因为我们完全可以做到这一点。

第一步,调整噪声中的Value1D、Value2D以及Value3D类函数的结果值。简单的乘以2再减去1。

然后从TextureCreator,SurfaceCreator以及SurfaceFlow中删除Value噪声的有效性检验。在得到一个噪声样本后,我们现在令它变为原来的二分之一。

5c2adc2f242601c150753cf1bb72a543.png

并且,为了使得TextureCreator保持在0到1的范围,我们需要加上½。 sample=sample*0.5f+0.5f;根据这些方法,将我们新的噪声类型添加到噪声脚本最前的枚举中。

f0d94937f5234c5cce5102e4e2257cdc.png

现在我们可以添加占位符方法以及它们的方法数组,并将其纳入在方法集中。这就允许了编辑器里的inspector可以去选择Simplex Value噪声。

b60d34a1eb17eaada7e18ade4a0102c7.png

1维

我们从比较简单的一维空间开始考虑。Simplex网格和超立方体网格在这种情况下是等价的,所以我们只需要抓住样本点的整数部分来获得其左边的晶格点。

65c6578a6ac155f1abb41f5a4d0b10ed.png

为什么导数可以非零?

你是如何找到衰减导数的?

让我们聚焦于线段左端的加成。当我们到达右端的时候,它应该从1开始并降至为零,就像常规的Value噪声一样。我们需要再一次确保该衰减函数的一阶导数和二阶导数同样下降为零。不同的是,该导数并不需要在一开始就为零,它们只需要是连续函数即可。除了考虑这些之外,我们需要一个基于网格交点的距离,且能在任何空间维度都可以有效的函数。虽然我们可以计算出实际距离,这将需要在更高的维度中执行平方根操作,所以我们宁愿避免计算实际距离。故而,我们可以用距离的平方项来操作么?如果可以这样做的话,我们会自动得到径向对称的衰减函数。最简单的衰减函数就是1 - x2。它的一阶导数与二阶导数分别是 -2x以及-2,当x为1时导数值不为0,所以该衰减函数并不适用。如果我们将该函数整体进行平方呢?然后我们就会得到(1 -x2)2,它的值应该仍然会降到零。该函数的一阶导数为-4x(1 - x2),其导数值可以降至为零,以及二阶导数-4(1 - x2) + 8x2,不会降至零。所以让我们更进一步的尝试三次的函数,(1 - x2)3。这个函数的一阶导数和二阶导数分别为-6x(1 - x2)2和-6(1 - x2)2 + 24x2(1 - x2),二者均可以按我们所需降至零。

40ed8730b356707eb2a8bb173dfca6c4.png

(1 - x2)n函数图像,n值从1到3

所以(1 - x2)3才是我们所需的衰减函数。让我们一步步对其进行计算并使其图像化。同时不要忘记,我们必须将最终的结果转换到-1 至 1的定义域。

a26786120d920c7111afc20caca18448.png

33b8083682ad4b0ac32136cf03cdc0ff.png

1维中的衰减效果,频率为4

现在,我们可以用代码编写方法去计算其中一端, 然后再执行一遍然后得出两端的结果。于是,在我们以独立的方式用代码计算结果的一部分后,并将其变为两倍,这样我们就可以得到两个点了。

3ade392918fb2cc88fc28bf499c9e964.png

33330c9d95464c91a66857c1817a06f3.png

将两部分叠加

正如你所看到的,结果在线段两端为满值而在中间的部分则弱了近一半。这种差异在更高的维度空间中则会变得更加显著。

将其转化为Value噪声的剩余步骤就是对其散列值进行因式分解。

3f762d8b6feddba468454aa17f9face0.png

3b8222b05fac8bbcda35349693b9502b.png

将Simplex Value噪声与Value噪声,频率为8

尽管衰减函数生成了更多的波段,但是最后的结果仍然与常规插值的Value噪声看起来非常相似。

当然,我们仍然不得不加入导数的计算,但是我们把这一步放到稍后再说。

2维

对于2维的噪声来说,我们同样以图像化衰减函数开始考虑。在1 维空间的方法基础上, 我们加入y轴,并且我们用和x轴一样的方式来处理y轴。

4cbcfdf8d1b4450b3e244744275d48ce.png

d1fff212226398bfdfc6ca616fd20158.png

二维空间正方形中的衰减效果,频率为2

我们现在可以在沿着主对角线方向上,看到每个正方形的两个角上都有径向下降的衰减。然而,这些角落本身并不是白色的。这是因为另一个角的衰减函数在该点上是负的,而这种情况在1维空间中是不可能发生的。我们不希望有负数点的分布,所以只考虑它们大于零的情况。

aae1fafb263cd02566414bbc90e4e292.png

169a43a5df4c6477084b385e3e89d4a9.png

不再有负数分布时的情况

当然,我们并不想得到正方形,我们想要的是三角形。那么我们要如何确定哪些三角形包含着样本点呢?以及我们应该如何对该三角形的角进行定位呢?我们知道,2 维空间可以用等边三角形进行平铺。我们也知道,2 维空间同样可以用正方形进行平铺,而且我们已经多次使用过了正方形。如果我们可以在两种图形的平铺过程中进行转换,这将会变得非常方便。

如果你把一个正方形沿对角线对折,你最终会得到两个三角形。这些都是等腰直角三角形。如果你沿着相同的对角线缩小这些三角形,在某一点上它们就会变成等边三角形。所以,如果我们对方形网格进做适当的坐标偏斜,并分裂这些方形网格,我们最终就会得到一个等边三角形网格。

要沿着主对角线方向进行拓展,意味着我们需要找出沿着这条线的各点所在。对于任何一点 来说,它等价于x + y。缩小本身的话可以通过都减去该点两个坐标的s(x + y)来实现。我们必须选择合适的比例因子,才能在最终得到等边三角形。

8caba0477ed05da4fbfa40b43724a707.png

以坐标偏斜进行网格转换

让我们定义一个三角形:A = <0, 0>, B = <1, 0>,以及C = <1, 1>。A点固定不变,但其他两个点将会被分别转换为 B = <1 - s, -s>以及C = <1 - 2s, 1 - 2s>。为了得到一个等边三角形,三角形的三个边的长度必须相等。

在没有被固定比例缩放的三角形中,从A到B的距离——或者直接表达为|AB|——为1。B和C之间的距离|BC|与|B|是一样的,所以我们可以忽略。从A到C的距离——或者直接表达|AC|——为 √2。而对于转换后的三角形,这些边的长度尚未知。

3d986476c0813432fcdea91fce566311.png

边与边的对比

什么是二次公式?

我们必须找到一个s的合适值,以便于在固定比例之后使得|AB| =|AC|。或者,我们可以使得两边长度的平方相等,即|AB|2 =|AC|2,这意味着我们就不需要使用平方根了。一个点的平方长度直接记为x2 + y2。

我们另|AB|2 =(1 - s)2 + (-s)2 = 2s2 - 2s + 1.

再另 |AC|2 =2(1 - 2s)2 = 8s2 - 8s + 2.

将这些等式化简为6s2 -6s + 1 = 0。使用二次方程求解,我们得到两个解s = (3± √3) / 6。我们的这两个解都是有意义的,因为你可以创建一个正等边三角形以及一个负等边三角形。为了获得正等边三角形,我们只选择最小的解,即为(3 - √3) / 6。

privatestaticfloatsquaresToTriangles = (3f-.Sqrt(3f)) /6f;

你是如何推倒出那些等式的?

但是我们怎么反过来从三角形转换到正方形呢?为此,我们考虑转换后的C点。它必须从回到<1, 1>,为此我们必须到某个新的s值。

我们另x + 2sx = 1,这意味着s = 1 / 2x - ½。我们知道 x = 1 - 2((3 - √3) / 6),且它等于√3 / 3或 1 / √3。将其替换代入,我们得到s = 1 / (2 / √3) - ½ = (√3 - 1) / 2。现在我们最终都得到了二者的转换因式。

所以,为了很容易的搞清楚我们所讨论的一对三角形,我们首先要使得网格坐标偏斜,让它成为正方形。我们利用分部法来确定出晶格点。

eb3e0518cdc7645abfbfff4e3e1f5c05.png

而为了获得实际的角点来确定它们之间的距离,我们必须使它们恢复坐标不偏斜的状态。

bb43265399f0a04f23819374f87e3bd8.png

2e5b725347de92fac6f92fea51a2a4c1.png

坐标偏斜过的网格

你是如何计算长度的?

我们现在看到的菱形是由两个等边三角形而不是由正方形组成的。我们也可以看到,这个衰减函数下降的还不够快。目前在距离为1时会下降到0,然而当距离与三角形的高相等时,它也应该为0。

一个等边三角形的高等于它的边长乘以√3 / 2。所以我们必须先确定边线的长度。我们已经知道, s = (3- √3) / 6时,|AB|2 =2s2 - 2s + 1,这导致了|AB|2 = ⅔。这是正方形边长的次方,所以实际的边长应该是√⅔或√6 / 3。这意味着三角形的高是√½或√2 / 2。

所以我们的衰减函数在距离的平方项为½时应该为0。这意味着我们应该使用½- x2 - y2作为基础值,而不是1 - x2 - y2.。

当然,这也意味着现在的衰减函数从½3 = ⅛开始,所以我们必须扩大最终结果进行抵消。

0ee2d225f221bd0163bca452f891d292.png

正确的衰减效果,未按比例缩小的 对比 按比例缩小的效果

下一步是确定哪两个三角形是我们所需要探讨的,要么是底部,要么就是顶部的三角形。这在正方形网格内部是很容易做到的。如果我们的X坐标大于或等于Y坐标,然后我们就处于底部的三角形。这个三角形的对应顶角坐标为 <0,0>, <1, 0>,以及 <1, 1>.。另一个三角形位于该三角形的上方,且对应顶角坐标为<0,0>, <0, 1>, 以及<1,1>。由于我们已经沿着主对角线囊括了顶角,,我们只需要添加还未纳入的角即可。

64d2fd4f3a0969f4239df4e3951fa5a7.png

确定你所处于的是哪一个三角形

ec2d5ba207a12d841f32bca4e9215822.png

20c9066778b86e872ead14bde6c7b2fa.png

完整的三角形为了使其变成真正的Value噪声,我们必须把散列值的因素纳入结果考虑。

d33b6a4d8ff7f4e9d41c50e26f698f82.png

然后下一步就是再次调整比例大小。

96e1871ebd0097da8c5bab415132a65e.png

2维空间中的Simplex噪声与Value噪声,频率分别为2,4和8正如你所看到的,2维空间中的Simplex噪声与放置于三角形网格或者蜂窝网格中的球体非常相像。它显示了和2维空间中Value噪声一样的散列模式,但是Simplex噪声是沿着主对角线进行了坐标偏斜。

3维空间

我们可以在3维空间中同样使用在2维空间中建立网格的方法,但是在这种情况下,我们使用的是立方体以及四面体。然而,3 维空间是不可能使用正空间四面体来进行填充的。所以我们必须使用坐标偏斜的四面体才可以进行填充,这就意味着并不是所有边都具有相同的长度。一个立方体可以分割为六个四面体,就好比一个矩形可以分割为两个三角形。每个四面体都有一条边沿着立方体的主对角线,从<0, 0, 0> 到<1, 1, 1>.。如果你必须沿着立方体的三条边行走,你有多少种方法可以在两个点之间行走呢?答案是6种方法,每个方法定义了四面体的三边或更多边。除此之外,每个四面体有两个边与相邻立方体面的对角线。

248e48db52f036985ee6fa576fa187d8.png

一个立方体中的六个四面体

我们从三个不同的边长开始入手。让我们定义一个四个点的四面体:A =<0, 0, 0>, B = <1, 0, 0>, C = <1, 0,1>,以及D = <1, 1,1>。应用一个未知的比例因子,我们得到了三个转换点。

B = <1 - s, -s, -s> ,以及 |AB|2 = 3s2 - 2s + 1.

C = <1 - 2s, -2s, 1 -2s>,以及|AC|2 =12s2 - 8s + 2.

D = <1 - 3s, 1 - 3s, 1- 3s>, 以及 |AD|2 =27s2 - 18s + 3.

bd2130236c95960c4b1731f9f2d39e07.png

偏斜网格坐标以及边的对比

让我们实验一下|AB|2 =|AC|2。这使得9s2 -6s + 1 = 0 以及s = ⅓。我们可以最终得到的长度的次方是多少呢?|AB|2 =|AC|2 = ⅔, 以及 |AD|2 =0。这并不适用,我们并将四面体转化为三角形。

所以,相反我们再次实验|AB|2 =|AD|2。这使得 14s2 -16s + 2 = 0以及s= 1/6。这使得我们正方形的边长为 |AB|2 =|AD|2 = ¾,以及|AC|2 =1。这可以适用,也使得最长边和最短边得以匹配相等,正与我们在2维空间中所做到的一样。

最后一个选择是实验|AC|2 =|AD|2。这使得15s2 -10s + 1 = 0以及 s = (5- √10) / 15。这导致了|AB|2 =4/5,以及= |AC|2 =|AD|2 = 1 +1/5。这也可以适用,尽管边长之间的差异会更大。

所以,让我们使用⅙的比例因子,将立方体分割为四面体。这些四面体均有四个等腰三角形组成。每个三角形都有一个长边的长度为1和两个短边长度分别为√¾或者√3 / 2。

为了从另一个方向上进行比例调整,我们考虑其中转化点D的一个组成部分。这一次我们另x + 3sx =1,以及x = 1 - 3(1/6) =½.。所以s =⅓。

让我们直接将2维空间中所用的代码复制过来,除去三角形检验后加入到3维空间中,并且代入我们新的比例因子,看看会发生什么。

feb5287fc53a7ed3764d38fef806a441.png

89e25e874c8bbcac48c64a6b7d2c21ce.png

沿着主对角线衰减效果,频率为2和8

你是如何计算高的?

这个衰减效果看起来非常不错。检查了表面三角形的几何结构表明,我们所得到的四面体的高度为√½。因为这与我们在2 维空间情况下的结果是一致的,所以我们不需要衰减基础值。在边长为√¾时,它还等于正四面体的高度。

ec3f16a9a58083ad863da93daeb410b5.png

确定四面体的高度,与正四面体进行对比现在我们需要确定我们所处的是哪个四面体。这与在2维空间中是一样的,但是现在我们需要比较的是三个组件而不是两个。所以,让我们先在主坐标轴上找出最接近的顶角。

9e8aeef7c3307a92c57007033c445021.png

三个角的衰减效果

为了得到第四个和最后一个角,我们不得不重复这一过程并且加入第二最近的角。

78b66b8c59f28036f66916ae570d1e79.png

确定还需包含的另外哪两个角

51c6599f43f4e8abd3cb7646d4a69359.png

a8a3a0e6fe7af3079e0a6810a7eba977.png

完整的四面体衰减效果

我们再次得到了三角形网格,但该网格与2维空间中的网格看起来并不一样。这是因为四面体并没有与主坐标轴中的任何一条坐标轴对齐。它们遵循着主对角线,所以当以与坐标轴为水平的面为参照时,你看到的是一个倾斜的切开四面体,这导致了非常明显的对角线变动。

所以,加入散列值会是什么样子的呢?

9602b65a3461ad0b161825fa717a8b43.png

所不要忘记再次调整结果中的比例。

cc2fb6877e00d11c952b25d334425bd8.png

完整3维空间中的Simplex Value噪声,频率分别为2,4和8

1b5c1d4348c2f25f9ddf092e7a5b7c89.png

沿着Z轴移动与常规的Value噪声不同,当沿着Z轴移动时,这种模式的噪声实际上并没有发生外观的改变。这些圆圈沿着对角线向下排列,但在经过晶格边界时,它们并不是在模糊与清晰之间交替。而实际上,这种情况是有发生过的,虽然不是在一次同时发生的。相反,对角线上会有清晰与模糊的交界。就像是基于超立方体的Value噪声,绕着X轴与Y轴旋转。

16f29634b838757b07da2f11e342e32c.png

Simplex Value噪声与 Value噪声,沿着主对角线旋转

导数

所以,导数究竟是怎么样的呢?非常幸运,导数是非常简单的。衰减函数(1 - x2)3 的导数是 -6x(1 - x2)2。将散列值分解代入,你就得到了1维空间的导数。

d448e93b52eabcb9ca20a369537d959c.png

当然,我们仍然需要调整频率值,所以,在返回最终结果值之前,记得立即调整频率。实际上,为了以便我们不会忘记这一步,还是立即调整2维和3维空间的频率吧。

sample.derivative *= frequency;

f74c8013cea4759ced03e604aa9c964a.png

1维空间中的Simplex Value噪声与表面分析法线对于两个维度来说,我们可以使用相同的方法。而导数公式组件唯一的区别就是两个维度应该用哪一个组件来进行相乘。由于其余的公式是相同的,或许我们只需要计算一次即可。

adc85cbd8067c035a6829bf005af39d3.png

对3维空间来说也很容易计算导数。

e091fe41fca5160c425c0aee5d44a714.png

52d750b61ffa7cc8ad4103604d80d781.png

2维与3维空间中的Simplex Value噪声与表面分析法线

Gradient噪声

是时候该解决Gradient Simplex噪声了,我们可直接将其命名为 Simplex 噪声。所以,我们添加了一个新的噪声类型。

1116f7b5da13dc8463f2cf5d9345a0f8.png

以及当然,我们还需要调整方法数组。

1beaa01be238e78bb2a291ff9e9bd343.png

现在,我们复制和重命名Simplex Value 噪声的方法。确保你所调用方法是属于它们自己部分的方法。我在这里只展示1 维空间中的变化情况。

9e51d256f56dc724f7e30e0c2c1926c0.png

1维

继续从1 维空间开始探讨,现在,我们还必须检索1 维空间的梯度而不仅仅是散列值。正如Perlin噪声一样,我们通过梯度向量与角到样本点向量的点积来计算梯度值。在1 维空间中这就是一个简单的乘法。同样与Perlin 噪声相同,梯度现在必须添加与梯度向量相乘的衰减函数。

afc1de53e08d04a18056dfe801bf96e8.png

现在我们必须确定噪声的最大值。与Perlin噪声相似,当线段两端的梯度向量指向彼此时,最大值沿着线段中间达到。这意味着最大值是2x(1 - x2)3,当x =½时,最大值也就是27/64。所以我们必须把最终结果除以这个值,这意味着就是乘以64/27。

9a12acc25d8e136be025e7a5326c28d1.png

1维空间中的Simplex噪声与Perlin噪声

8ee04d763339cd466c85ce43b758ed38.png

1维空间中的Simplex噪声与表面分析法线

2维空间

在2 维空间中也是一样的。得到梯度向量,计算点积,把它们包含在值和偏导数。

1b237e443abe0235a91ee15f06634b99.png

现在的问题是最大值在哪儿呢?它是在三角形的某条边上,或者是在三角形的中心吗?让我们把这两者都计算一下。

要记住,三角形边长是√⅔或√6 / 3。所以我们得到2x(½ - x2)3 ,当x为√6/6,最大值为 √6 / 81。

接下来,在等边三角形中,从顶角到三角形中心的距离等于它的边长乘以√3 / 3或着√⅓。故而在顶角处我们可以得到3x(½ - x2)3, 当x = √2 / 3时,结果值为125√2 / 5832。由于这个值比另一个值稍微大一点点,这就是我们理论上的最大值。它的乘法逆元(倒数)可以写成2916√2/125,这个值就是我们最终的缩放比例。

761e91a3abec1e21f55ed49f0eeaf26b.png

将可能的最大值图像化。该白色中心点即为至高点。

现在我们只需在最后将其化为因式,我们就完成了这个步骤。

91f3ac0934f4ef04397fdb13490bb416.png

图像2维空间中的Simplex 噪声与Perlin噪声,频率为2,4和8一个额外的问题是在实际中我们是否完全覆盖了-1到1的范围?因为我们并不认为,我们只使用了八个梯度向量。事实证明,我们的八个45度旋转梯度向量所产生的最大值,可以非常接近-1到1的范围。所以确实如此,我们有效地实现了覆盖整个-1到1的范围。

82b07dceb81fa603df85d0fcfa4a5ba6.png

2维空间中的Simplex噪声与表面分析法线

3维空间

到了3维空间中,Simplex噪声所需进行的改变也是理所当然的。

b072de8d1301601d7ba74e1a7a933545.png

为了获得最大值,我们注重观察偏斜四面体的短边中点是意义重大的。我们显然在其他更长的边上是找不到最大值的。在一个等边三角形中,一条边的中点和中间位置的区别是非常小的。把等边三角形变成一个直角三角形时,只增加了两个顶角之间的距离,所以中间面也就出来了。四面体的中心的距离是更长的,所以我们可以忽略它。

31c780896b317945b5a7977208b8f99d.png

将可能的值图像化。白色中心点即为至高点。由于最短的边长度是√3 / 2,我们得到2x(½ - x2)3,当 x = √3 / 4时,最终结果是125√3 / 8192。这个数的倒数可以写成8192√3 / 375,所以这就是我们所需的比例因子。实际上,因为我们的3维梯度数组包含有向量长度为√2的向量,所以我们必须通过将其因式分解和分母进行抵消。

2a26fa3e859454ffd76a38340d5c9bc0.png

在我们用最终结果值乘以该值之后,我们可以检测一下噪声。

9bf8cb82861570cdab323f8e11f48428.png

范围不完整的3维Simplex噪声噪声看起来不错,但事实证明,我们当前的梯度向量集并没有占据完整的范围。最大的振幅似乎大约是0.85而不是1。我们该如何才能修正这个问题呢?我们知道,所有的四面体都有一个最短的边能与主对角线对齐。所以如果我们沿着主对角线上加入两个截然相反的向量,我们就可以覆盖-1 到1的整个范围了。我们目前所使用的梯度数组是由Ken Perlin设计的。它包含指向立方体12条边中点的所有向量。为了将数组大小增加到16,我们重复四个数组。为了包括进主对角线,我们必须添加指向立方体两个角的向量。为了保持对称,我们必须添加所有的八个角向量。十二条边再加上八个角,我们就有了二十个向量,但20并不是2的次幂。然而,如果我们把边加入两次的话,我们最终会得到32个向量。为了让这些向量都保持一样的长度,我们必须将它们规范化处理。

8b31b78751d8abb97996bd4dc46864e8.png

所以,现在我们可以用这些新的梯度向量来代替Perlin噪声所使用的那些向量了。

7f6602a30070f615c2f060db4b9f37ae.png

而且现在,我们不再需要除以√2了。

有了这些,我们的噪声就可以覆盖整个范围了。

615f9c8de2931e0765b882d8fd62c18a.png

3维的Simplex噪声,频率为2,4和8

9ebf53c2705d8b5ff87125aa288b36b6.png

沿着Z轴移动

6c036715dc50129956f45cb4750407e8.png

Simpx噪声与Perlin噪声,旋转观察主对角线

ee5c8c4207d1a516faf87dc4aeb24749.png

3维Simpx噪声与表面分析法线以上这些就是如何做Simplex噪声的方法了。

你喜欢本教程么?与我一起来创造更多吧!

下载内容

simplex-noise-finished.unitypackage 已完成的项目

Simplex噪声没有申请专利吗?

当人们提到Simplex噪音已经申请了专利时,他们指的是美国专利6867776 B2,“Perlin噪声标准”,为诺基亚所拥有。如果你想知道其中的具体细节,可以上网查询,我会给一个简单的总结。

已经声明的专利都是在生成不可见网格工件纹理的环境下产生的。这意味着Simplex噪声并不侵犯这些专利中的任何一个,因为Simplex噪声的网格是显而易见的。而且,它们是关于3 维或者是更高维度的,所以并不包含1 维和2 维的Simplex噪声。

第一个独立声明是关于3 维晶格点的二进制处理。简单地说,如果你执行多个散列值和梯度数组查询以及计算向量点积,你就并不侵犯专利。

第二个独立声明确切关于在3维空间中用斜率⅓来确定simplex网格点。所以只有3维Simplex噪声侵犯了这一点。围绕其进行探索,需要找到一个不同的方式来构建网格,或者是使用更高维度的噪声。它有一个补充声明,增加偏斜坐标层得到单位立方体的顶角。其进一步的声明包含了评估整个Simplex噪声。

第三个独立声明是关于将一个n维超立方体借助边缘穿越分割成为n !个单形。它提出的要求是,n是至少为3,所以它适用于3 维以及更高维度。在实际操作中不需要将超立方体分割成单形。它有一个补充声明曾提到,它可以计算复杂度 O(n2)。

这项专利的有效性是什么呢?这是一个法律问题,我并不知悉。没有人知道除非它已经通过法庭的审定。我从来没有听说过它被执行或者是授权,尽管Simplex噪声的多个实现常年以来都处于随时可用的状态。

如果你想更加安全合法的使用它,就不要在3维空间和你的作品中使用simplex网格,添加梯度噪声代码。或者联系诺基亚。

为什么导数可以非零?

当我们处理关于一个点的距离的递减函数时,无论你朝哪个方向远离该点,它的值都是递减的。相反,从任何方向接近于该点时,函数值是递增的。当你到达点后继续移动过该点,你就会突然从递增变为递减。这意味着变化率并且该函数的一阶导数从正变为了负,这意味着它在中点时应该为0。然而,对于一阶导数而言,其变化率必须减少,这表明二阶导数在中点是可以为负的。故而一阶导数为零,但是二阶导数不必为零。

你是如何找到衰减导数的?

(1- x2)2的导数可以运用链式法则进行快速提取,正如在噪声导数教程中所解释的内容。一阶导数变成了 2(1 - x2)(-2x) = -4x(1- x2)。我们继续积的求导法则计算二阶导数,结果为 (-4)(1 - x2) + (-4x)(-2x)= -4(1 - x2) + 8x2。我们让它保留这种表达式,所以我们可以很容易的看到,当x=1时,一阶导数变成了零,而二阶导数并没有成为零。

(1- x2)3的一阶导数同样变成3(1 - x2)2(-2x) = -6x(1- x2)2.。二阶导数现在需要同时应用链式法则以及积的求导法则。正如我们已经所知道的,我们可以直接插入 (1 - x2)2 导数部分。最终的结果是 -6(1 - x2)2 + (-6x)(-4x(1- x2)) = -6(1 - x2)2 +24x2(1 - x2),它正可以合我们所需降至为零,且当x = 0时结果为-6。

什么是二次公式?

二次公式是解决形如 ax2 + bx + c = 0的方程的一种办法。它指出 x = (-b ± √(b2 - 4ac)) /2a.。这样的二次方程可以描述有两个零点的抛物线,这就是为什么x可以有两个解的原因。如果 b2 -4ac这个部分变成了零,那就只有一个解,而且抛物线的顶点为零。如果它抛物线根本没有零点,那么 b2 - 4ac会是负的,你最终也会得到负值。

我们另a=6,b=-6,以及c=1,我们所得到的的解为(6±√12)/ 12 =(6±2√3)/ 12=(3±√3)/ 6。

你是如何推倒出那些等式的?

为了将s从 x + 2sx = 1中分离出来,我们除以 2x得到 ½+ s = 1 / 2 x,然后将½移项到另一边,让s = 1 / 2 x - ½。

接下来,用x代表C的x坐标的转换值,也就是1 - 2 s的第一个比例因子s = (3 - √3) / 6.。这使得x = 1 - (3 -√3) / 3 = 1 - 1 + √3 / 3 = √3 / 3 = 1 / √3.。

现在我们知道新的比例因子是什么 :即 s = 1 / (2 / √3) - ½ = √3 / 2 - ½ = (√3 - 1) / 2.

你是如何计算长度的?

我们另2s2 - 2s + 1 ,且 s = (3 - √3) / 6。第一次分离 s2 = (9 - 6√3 + 3) / 36 = (12 - 6√3) / 36 = (2- √3) / 6。同时替换 s和 s2,最后我们得到 (2 - √3) / 3 - (3 - √3) / 3 + 1 = -⅓ + 1 = ⅔.。

这就是正方形的边长,所以三角形的高就是√⅔√3 / 2 = √2 / 2.

你是如何计算高的?

首先确定三角行面的高度,也就是它长边的中点到它对应顶角之间的距离。我们可以沿着这条高线减去该三角形的一半,最终就得到了一个直角三角形。然后勾股定理就可以让我们得到想知道的边长, √(√¾2 - ½2),也就是√½。

现在考虑四面体中与长边相接的两个面。一个面呈水平状态,另一个面树立向上。那么上顶角的高度是多少呢?看看被定义的三角形,是由面的两条线以及四面体中两个不相邻顶角之间的边围成的。这个三角形的三边长分别是√½,√½和1。这是一个直角三角形,这意味着两个面之间的夹角为90°。第二个面是垂直于水平面的,所以四面体的的高也为√½。

在一个正四面体的中,它的高等于它的边长乘以√⅔。所以, √¾√⅔ = √(6 / 12) = √½.

【版权声明】

原文作者未做权利声明,视为共享知识产权进入公共领域,自动获得授权

今日推荐

1.加入GAD程序猿交流基地

获取行业干货资讯,观看大牛分享直播

2.直接领取60G独家程序资料库,地址在小编朋友圈

包括腾讯内部分享、文章教程、视频教程等全套资料返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值