
基于水蚀沉积的高度场地貌平滑冲积扇与盆地泥沙堆积算法实现在程序化地形生成的实践中很多开发者往往把全部注意力放在了“侵蚀Erosion”的破坏力上。大家喜欢看着算法如何在平滑的山坡上冲刷出一道道陡峭的深沟以此证明自己的地形具备“地质真实感”。然而一旦把镜头拉向山脉与平原交界的山脚地带画面的违和感立刻暴露无遗山谷两侧的岩石被切削得刀削斧劈般锋利但在峡谷的出口处地形却突兀地戛然而止直接与一马平川的平原相撞没有任何过渡或者在低洼的盆地中央地形依然保留着噪声函数原本的杂乱起伏完全没有经年累月泥沙淤积形成的平坦湖床。真实世界的地貌演化永远是一场严格的质量守恒博弈高处被流水刮走的一千吨泥沙绝不会凭空蒸发消失在虚无中它们必然会顺着水流被搬运到地势低洼平缓的区域重新沉淀下来。正是这种泥沙沉淀机制孕育了自然地理学中壮丽的冲积扇Alluvial Fan与沉积盆地。要补全大世界地形的自然真实度必须在水蚀管线中构建精确的泥沙承载力衰减模型配合**自然休止角Angle of Repose**重力滑动塌陷在峡谷出口处自然堆叠出优美的扇形冲积平原。冲积扇形成的流体力学机理在地理流体力学中泥沙的搬运与沉积取决于水流动能的阶跃突变峡谷狭管效应高山深谷中两侧山壁收紧水流落差极大流速 $V$ 极高。此时水流的泥沙容量上限 $C \propto V^2$ 处于极大值泥沙处于全饱和悬浮搬运状态。出山口动能断崖跌落当水流冲出狭窄的山谷隘口、到达宽阔平坦的山麓平原时水流摆脱了山谷的侧向约束向四周漫流展开流截面积瞬间膨胀十倍水深变浅摩擦阻力急剧增大流速 $V$ 发生断崖式下跌。泥沙过饱和扇形沉积流速的骤降导致水流的泥沙容纳能力在短短几十米内归零。原本携带的大颗粒粗砂石最先沉淀在山口根部细泥沙顺着发散的辫状水系向外围推移在山口正前方自然堆砌出一个向平原舒展延伸的半圆锥形扇面。自然休止角Angle of Repose热力侵蚀塌陷当泥沙在某个网格点集中沉淀堆积时如果只是一味往上加高度高度场会出现极其反常识的“细长针状泥柱”。松散的干燥泥沙颗粒在重力作用下能够维持的最大稳定倾斜角被称为自然休止角Angle of Repose / Talus Slope在碎石泥土介质中通常为$30^\circ$ 到 $35^\circ$。一旦局部沉积造成两点之间的倾角超过了临界休止角重力剪切力将战胜颗粒间的摩擦力触发松散泥沙沿坡度向低处的崩塌滑动Thermal / Talus Erosion直到坡度重新平稳收敛到休止角以内。这套机制能将生硬的阶梯沉积物自然抹平成平缓连绵的斜坡。生产级冲积扇沉淀与休止角松弛 C 实现下面展示在离线高度场生成流水线中运行的泥沙沉积与休止角塌陷算法核心实现#include vector #include cmath #include algorithm struct HeightfieldGrid { int width; int height; std::vectorfloat elevation; float Get(int x, int y) const { return elevation[std::clamp(y, 0, height - 1) * width std::clamp(x, 0, width - 1)]; } void Add(int x, int y, float delta) { elevation[std::clamp(y, 0, height - 1) * width std::clamp(x, 0, width - 1)] delta; } }; class AlluvialFanDepositionPipeline { public: // 泥沙休止角松弛模拟 (Thermal Weathering / Talus Relaxation) static void ApplyTalusRelaxation(HeightfieldGrid grid, int iterations 10, float talusAngleDegrees 32.0f) { // 临界高度差阈值 网格物理间距 * tan(休止角) const float gridSpacing 1.0f; // 假设每格跨度 1 米 const float maxStableSlope std::tan(talusAngleDegrees * 3.14159265f / 180.0f); const float maxStableDelta maxStableSlope * gridSpacing; const int dx[4] { 1, -1, 0, 0 }; const int dy[4] { 0, 0, 1, -1 }; for (int iter 0; iter iterations; iter) { for (int y 0; y grid.height; y) { for (int x 0; x grid.width; x) { float currentH grid.Get(x, y); for (int d 0; d 4; d) { int nx x dx[d]; int ny y dy[d]; if (nx 0 nx grid.width ny 0 ny grid.height) { float neighborH grid.Get(nx, ny); float diff currentH - neighborH; // 若坡度超越临界休止角触发泥沙重力滑坡 if (diff maxStableDelta) { float excess (diff - maxStableDelta) * 0.5f; grid.Add(x, y, -excess); // 高处滑落 grid.Add(nx, ny, excess); // 低处堆积 } } } } } } } // 在峡谷出山口执行扇形放射状泥沙定向沉淀 static void InjectAlluvialSediment( HeightfieldGrid grid, int canyonExitX, int canyonExitY, float flowDirX, float flowDirY, float totalSedimentVolume, float fanRadius 45.0f ) { // 归一化出山口出水方向 float len std::sqrt(flowDirX * flowDirX flowDirY * flowDirY); if (len 1e-5f) return; flowDirX / len; flowDirY / len; int r static_castint(std::ceil(fanRadius)); // 遍历山口前方的半圆扇形区域 for (int dy -r; dy r; dy) { for (int dx -r; dx r; dx) { float dist std::sqrt(float(dx * dx dy * dy)); if (dist fanRadius || dist 1.0f) continue; float dirDot (dx * flowDirX dy * flowDirY) / dist; // 只在水流正前方 120 度扇形夹角内堆积泥沙 if (dirDot 0.2f) { // 沉积厚度随距离二次方平滑衰减 float distanceDecay 1.0f - (dist / fanRadius); float angularDecay (dirDot - 0.2f) / 0.8f; float weight distanceDecay * distanceDecay * angularDecay; float deposit (totalSedimentVolume * 0.005f) * weight; grid.Add(canyonExitX dx, canyonExitY dy, deposit); } } } // 堆积完毕后执行 5 次休止角松弛消除人工数学痕迹 ApplyTalusRelaxation(grid, 5, 30.0f); } };生产落地的避坑指南防止多次迭代导致山峰高度全局缩水休止角松弛如果无节制地全图跑几十次原本陡峭高耸的孤峰会被整体验算坍塌成平缓的馒头堆。必须在执行滑坡松弛前为高度场生成一张“岩石刚度硬度图Rock Hardness Mask”。对于山脊高耸的花岗岩露头强行将其休止角放宽至 $75^\circ$坚固不崩塌只对河谷周围松散的沙土地质应用 $30^\circ$ 的松弛塌陷。沉积平原与水面网格的高程相交防穿插在盆地泥沙沉积完成后如果有内陆湖泊存在必须确保沉积地表的平滑边界与湖泊水面的水平面Water Surface Plane保持微小的倾角递增。如果沉积物由于浮点精度与水面完全共面在光照着色时会产生极难修复的 Z-Fighting 深度闪烁。从侵蚀破坏到沉积新生才构成了完整的自然地理闭环。当玩家策马冲出险峻幽暗的峡谷山口迎面展开的是一片平缓舒展、铺满细碎砂砾与肥沃泥土的冲积平原代码构建的世界才拥有了令人心魄的辽阔与自洽。