这篇文章深入探讨了我的程序地球模拟的实现,完全用 GLSL 片段着色器编写。它在几分钟内模拟了类地行星的完整历史,模拟更新速度为每秒 60 帧。这个故事开始于四五十亿年前,一块熔岩……早期的地球是一颗原行星,炽热炽热,被小行星撞击造成严重撞击坑。因为我的地球模拟完全是程序生成的,没有预渲染的纹理,所以第一个任务是生成这个地形的地图。要计算给定纬度和经度的地形高度,首先转换为 3D 笛卡尔坐标:现在,随着小行星的大小各不相同,由此产生的陨石坑也是如此。为了适应这一点,着色器迭代了五个细节级别,将大小逐渐减小的陨石坑相互分层。为了使陨石坑具有逼真的崎岖外观,它混合了一些分数布朗运动噪声,并进行了缩放,以便最大的陨石坑对地形的影响最大。浮动高度 = 0.; for (float i = 0.; i < 5.; i++) { float c = craters(0.4 * pow(2.2, i) * p);浮动噪音 = 0.4 * exp(-3. * c) * FBM(10. * p);浮动 w = 钳位(3. * pow(0.4,i),0.,1.);高度 += w * (c + 噪音);}高度 = pow(height, 3.);陨石坑本身是在 3D 网格上生成的,从中雕刻出一个球体用于表面地形。为了避免明显的规律性,陨石坑中心使用散列函数从网格点获得一个伪随机偏移量。要计算给定位置的陨石坑的影响,取属于附近网格点的陨石坑的加权平均值,权重随着距中心的距离呈指数下降。火山口边缘由简单的正弦曲线生成。浮动陨石坑(vec3 x){ vec3 p = floor(x); vec3 f = fract(x);浮动 va = 0.;浮动重量 = 0.; for (int i = -2; i <= 2; i++) for (int j = -2; j <= 2; j++) for (int k = -2; k <= 2; k++) { vec3 g = vec3 (i,j,k); vec3 o = 0.8 * hash33(p + g);浮动 d = 距离(f - g, o);浮动 w = exp(-4. * d); va += w * sin(2.*PI * sqrt(d));重量 += 重量; } return abs(va / wt);} 最终程序生成的高度图如下所示:虽然相对简单,但在将低洼地区用水填满后,这个程序地形类似于科学家认为的早期地球的实际样子:水包含在其中被热量蒸发了,热量逸出并开始在地球周围形成的早期大气中循环。随着时间的推移,岩石冷却,水蒸气开始凝结成海洋。液态水流过地表,在地形中形成山谷,在其尾流中留下沉积物的堆积。山脉、海沟和熟悉的大陆地貌的形成需要构造运动模型。模拟随机生成板的种子位置,具有初始速度。这些板块通过一个简单的聚合模型随着时间的推移而增长,该模型随机选择相邻点并将它们添加到一个板块(如果它们尚未分配到另一个板块)。板块内的所有像素都存储板块移动的速度。聚合模型类似于扩散限制聚合(但没有扩散):板块的连续运动是困难的,因为它需要板块边界来解释以像素分数测量的运动。为避免这种情况,板以离散的时间步长移动,水平或垂直移动整个像素。每个板块的这些时间是随机的,这样平均速度保持在设定的速度和方向,并且相邻板块不太可能同时移动。当一个板块的某些边界像素移动到先前由属于另一板块的像素占据的位置时,就会发生板块碰撞。这会导致俯冲,通过简单地稍微增加碰撞位置的地形高程来模拟。尽管这仅发生在沿板块边界的像素处,但影响会通过简单的热侵蚀模型逐渐扩散到相邻像素,该模型将像素的高程推向其相邻像素的平均值方向。总而言之,这提供了对带有山脉的大陆形成的良好模拟(将在下一节中引入水力侵蚀将进一步改进):自然地形的崎岖外观在很大程度上是由河流流域的形成驱动的以熟悉的分支模式呈现风景。各种水流模拟很容易用于此任务,但这里的困难在于地形图的分辨率对于整个行星来说非常低。因此,该模型必须能够模拟宽度不超过单个像素的河流。 Barnes (2018) 提出了一个简单的模型来实现这一点。简单地说,每个像素检查它的八个邻居,以确定哪个方向的高程下降幅度最大(根据对角线邻居更远的事实进行调整)。这个最大坡度的方向是流出这个像素的水流向的方向。水最初通过降雨分布在细胞之间,然后在每个时间步长在相邻像素之间传输。侵蚀是由河流幂律驱动的:这里我们有位于当前单元格处的高程和水量,以及水流方向的坡度。海拔的下降有上限,这样它就不会低于水流到的位置。水流和侵蚀之间的相互作用导致河流流域在地形中的自然形成:通过为相连的水道着色(颜色由河口的位置决定),可以产生令人联想到真实流域地图的醒目可视化效果: