模拟水力冲刷

2020-06-23 23:07:09

水力侵蚀是水随时间改变地形的过程。这主要是由降雨造成的,但也有海浪撞击海岸和河流流动造成的。图1显示了一条小溪对其周围岩石环境产生的相当大的影响。在创建逼真的环境时,需要考虑侵蚀的影响。我以前曾尝试过程序地形生成,以生成分层体素渲染的场景,并演示立方体噪波。这些地形是非常基本的,没有考虑到侵蚀。因此,他们缺乏很多细节,使他们在仔细检查时是不现实的。

在这篇文章中,我将详细介绍一种简单而快速的方法,它近似于水力侵蚀的影响。这种方法的目的是创造可信的环境,而不是达到高度的现实主义。只要结果看起来很自然,为了速度,忠诚度可能会被牺牲。综上所述,该方法应做到以下几点:

当涉及到模拟侵蚀时,有几种不同的方法。所有的方法都模拟了相同的现象:水从高处流向低处,在流动的过程中侵蚀地形,在沿着自己的路径进一步前进时沉积沉积物。这一过程总是导致一些可识别的地形特征,如河流流经的沟壑和山谷,河流与目的地汇合的三角洲,以及小溪汇合成大河的冲积扇。在阅读有关此主题的文章时,我在研究文献中遇到了以下不同的策略:

侵蚀是通过跟踪地形上每个位置的水的位置来模拟的。为环境创建网格(或二维数组),并保持每个单元的水位和压力。更新时,压力决定水流向何处。流动时,水会移动沉积物。

侵蚀是通过在地形上投放许多模拟雨滴的粒子来模拟的。然后粒子沿着地形的坡度移动。他们可以随身携带沉淀物,也可以将其沉淀。

主要出于性能原因,我选择实现基于Drop的方法。因为大多数水滴不会流得很远,所以许多不活跃的水滴模拟可以提前终止,并且大部分处理能力将流向实际绘制出地形特征的水滴。对于每个更新周期,基于网格的模拟将需要模拟地形上的每个部分。

模拟中的水滴可以被视为雪球,而不是雨滴。在模拟的背景下,我认为这是一个更好的类比。雪球在落下时开始很小,但当它们滚下山坡时就会获得更多的物质。当它们变得太大时,它们就开始边走边脱落。当它们停止在山谷或海洋中滚动时,雪球就会碎裂,并将它们的物质留在地形上。

完整的侵蚀算法(在Javascript中)可以在下面阅读。此代码使用heightMap对象进行侵蚀。此高度贴图可以读取和写入,并且sampleNormal函数可用于获取曲面法线。这是从地形向上指向的3D矢量,因此可用于确定坡度方向和陡度。

/*让雪球侵蚀高度图*@param{number}x X坐标开始于*@param{number}y Y坐标开始于*/trace=function(x,y){const ox=(随机性.getFloat()*2-1)*半径;//X偏移量常数y=(随机性.getFloat()*2-1)*半径;//Y偏移量让沉淀物=0;//携带沉淀物的量让xp=。//上一个Y位置设vx=0;//水平速度设vy=0;//(设i=0;i<;maxIterations;++i){//获取当前位置地形的表面法线const faceNormal=heightMap.sampleNormal(x+ox,y+oy);//如果地形平坦,停止模拟,如果(faces eNormal.y=1)破裂,雪球不能继续滚动。常量侵蚀=侵蚀率*(1-faceNormal.y)*Math.min(1,i*iterationScale);//更改此雪球来源地的沉积物Map.change(xp,yp,沉积-侵蚀);沉积物+=侵蚀-沉积;vx=摩擦*vx+faceNormal.x*速度;vy=摩擦*vy+faceNormal.z*速度;xp=x;yp=y;x+=vx;y+=vy。++i)trace(随机性.getFloat()*width,随机性.getFloat()*Height);//模糊高度贴图以平滑效果sheightMap.blur();

变量ox和oy编码雪球的偏移量。它们用来读取具有一定偏移量的地形坡度,使雪球运动变得更粗糙,从而防止雪球路径过于收敛。

当曲面法线完全向上指向时(当该法线的y值等于1时),雪球就会终止。在实践中,这意味着已经到达模拟边缘的雪球或海底停止模拟那里。因为这些区域什么都不会发生,所以模拟侵蚀将是对处理能力的浪费。

更改沉淀量时,雪球将在其先前位置而不是当前位置编辑高度贴图。侵蚀和沉积发生在它的后面,以防止雪球自己挖进去。

模拟侵蚀后,将高斯模糊应用于高度贴图。由于这些示例中的高度贴图的分辨率较低,因此需要模糊来保持曲面足够平滑,以便在视觉上具有吸引力。

由于偏移量是在侵蚀时使用的,而且腐蚀率相当高,因此每个跟踪的雪球对地形的影响比看起来更像雨滴的较小节点对地形的影响更大。这会导致快速模拟,但会降低精度。

将上述算法应用于不同的雪球计数,结果如图3所示。该算法在浏览器中运行,源代码可以在GitHub上找到。按空格键将生成一个新孤岛。算法的起始材质显示在图3的第一张图片中。这个岛的形状是使用与我在分层体素渲染示例地形中使用的算法非常相似的算法生成的。虽然这个形状确实包含了一些细节和山脊,但它非常光滑,没有水力侵蚀的痕迹。

第二张图片显示了在同一个岛上投下了35.000个雪球。它们被随机、均匀地投放。由于起始形状的随机初始条件,形成了山谷和类似河流的结构,雪球在那里找到了到达大海的最快途径。35.000看起来可能是一个很高的数字,但请记住,到达海底或地图边缘的雪球会提前终止。大多数水滴不会落在岛上,所以只有一小部分水滴实际上会从图中可以看到的山谷中滚落下来。

第三张图片显示了掉下50.000个雪球后的同一个岛。与上图相比,没有新的细节形式,尽管地形特征更加明显。

最后一张图片显示了掉下100.000个雪球后的小岛。这显然太多了;山脊变得非常深,海岸非常崎岖。在这一点上,结果也开始看起来不太现实。这些山谷刻出了非常尖锐的地形地貌,这些地貌会自我侵蚀。

上图中的所有孤岛都可以在我的台式计算机上半秒内生成,算法在单个CPU线程上运行。因此,在大多数应用程序中,出于性能原因没有必要减少滚雪球的数量。就目前而言,该算法已经足够快了。

该算法提供了一种快速逼近水力侵蚀的方法。虽然现实主义不是优先考虑的,但当在不同的地形上测试该方法时,人们预期的侵蚀和沉积模式确实会出现。

由于代码运行速度非常快(与文献中可以找到的大多数替代解决方案相反),因此它可能适用于游戏中的过程性地形生成等应用程序。在这些应用程序中,希望快速产生结果,而结果不需要非常现实;它们只需要看起来可信即可。

该方法可推广应用于河床路径跟踪。许多雪球滚滚而过的山谷实际上是河流。当一个区域达到一定的滚雪球流量阈值时,就可以在那里形成一条河流或湖泊。

另一个有趣的补充是一个纹理,它可以跟踪地形上材料的侵蚀和沉积的量。然后这些数据可以用来给地形上色;如果沉积了大量的材料,沙子和小颗粒就会在那里堆积。几乎没有发生侵蚀的地区将看起来与严重侵蚀的斜坡不同。

该动画示例包含朝向岛屿海岸移动的波浪。除了澄清海岸的形状外,它们并没有真正起到防止侵蚀的作用,但它使场景更漂亮了。

首先,在岛屿海岸周围创建一张沃罗诺伊图。不是从点创建关系图,而是从形状创建关系图。岛上不在海平面以下的每一部分基本上都是沃罗诺伊图上的一个点。这篇博文解释了用于在GPU上生成Voronoi图的跳跃泛洪算法;这篇博客还解释了在构建图时使用形状而不仅仅是点。

创建Voronoi图后,图中的数据用于创建纹理,存储每个像素到最近岸点的距离和方向。图4显示红色和绿色通道用于存储方向矢量。矢量的大小编码了到海岸的距离,那里的黑色区域离海岸最远。

创建正弦波来表示全局波形模式。正弦波中的位置由朝向最近岸点的方向确定,并且波慢慢地朝向岸边移动。如果波浪是三维的,则可以使用朝向最近岸点的方向来计算波浪形状的表面法线。

最后,波浪被风格化,波浪图案被稍微分解,以给人一种所有波浪都是它们自己的实体的印象。