news 2026/10/7 8:59:38

基于水蚀沉积的高度场地貌平滑:冲积扇与盆地泥沙堆积算法实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于水蚀沉积的高度场地貌平滑:冲积扇与盆地泥沙堆积算法实现

基于水蚀沉积的高度场地貌平滑:冲积扇与盆地泥沙堆积算法实现

在程序化地形生成的实践中,很多开发者往往把全部注意力放在了“侵蚀(Erosion)”的破坏力上。大家喜欢看着算法如何在平滑的山坡上冲刷出一道道陡峭的深沟,以此证明自己的地形具备“地质真实感”。

然而,一旦把镜头拉向山脉与平原交界的山脚地带,画面的违和感立刻暴露无遗:山谷两侧的岩石被切削得刀削斧劈般锋利,但在峡谷的出口处,地形却突兀地戛然而止,直接与一马平川的平原相撞,没有任何过渡;或者在低洼的盆地中央,地形依然保留着噪声函数原本的杂乱起伏,完全没有经年累月泥沙淤积形成的平坦湖床。

真实世界的地貌演化永远是一场严格的质量守恒博弈:高处被流水刮走的一千吨泥沙,绝不会凭空蒸发消失在虚无中,它们必然会顺着水流被搬运到地势低洼平缓的区域重新沉淀下来。

正是这种泥沙沉淀机制,孕育了自然地理学中壮丽的冲积扇(Alluvial Fan)与沉积盆地。

要补全大世界地形的自然真实度,必须在水蚀管线中构建精确的泥沙承载力衰减模型,配合**自然休止角(Angle of Repose)**重力滑动塌陷,在峡谷出口处自然堆叠出优美的扇形冲积平原。

冲积扇形成的流体力学机理

在地理流体力学中,泥沙的搬运与沉积取决于水流动能的阶跃突变:

  1. 峡谷狭管效应:高山深谷中,两侧山壁收紧,水流落差极大,流速 $V$ 极高。此时水流的泥沙容量上限 $C \propto V^2$ 处于极大值,泥沙处于全饱和悬浮搬运状态。
  2. 出山口动能断崖跌落:当水流冲出狭窄的山谷隘口、到达宽阔平坦的山麓平原时,水流摆脱了山谷的侧向约束向四周漫流展开,流截面积瞬间膨胀十倍,水深变浅,摩擦阻力急剧增大,流速 $V$ 发生断崖式下跌。
  3. 泥沙过饱和扇形沉积:流速的骤降导致水流的泥沙容纳能力在短短几十米内归零。原本携带的大颗粒粗砂石最先沉淀在山口根部,细泥沙顺着发散的辫状水系向外围推移,在山口正前方自然堆砌出一个向平原舒展延伸的半圆锥形扇面。

自然休止角(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::vector<float> 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_cast<int>(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 深度闪烁。

从侵蚀破坏到沉积新生,才构成了完整的自然地理闭环。当玩家策马冲出险峻幽暗的峡谷山口,迎面展开的是一片平缓舒展、铺满细碎砂砾与肥沃泥土的冲积平原,代码构建的世界才拥有了令人心魄的辽阔与自洽。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 8:59:36

硬件原理图分析实战:从电源树到信号完整性的系统读图方法论

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 8:59:00

运放信号平移与电平搬移:双极性信号进单电源ADC设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 8:58:58

ESP32-S3全栈自造开发者状态仪表盘

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 8:58:35

OpenHarmony HDF驱动实战:MLX90614红外测温传感器I2C接入与调试

1. 从一颗红外测温芯片说起&#xff1a;为什么选MLX90614做OpenHarmony驱动实战做嵌入式开发这些年&#xff0c;温度传感器我经手过不少&#xff0c;从最基础的NTC热敏电阻到DS18B20&#xff0c;再到热电偶方案&#xff0c;各有各的适用场景。但第一次接触MLX90614的时候&#…

作者头像 李华
网站建设 2026/10/7 8:58:30

工业设备接入平台十年演进:协议、监控、日志与诊断的实践之路

1. 十年前&#xff0c;我们是怎么被协议碎片化逼疯的2013年前后&#xff0c;我接手了公司第一个设备接入平台项目。当时摆在我们面前的现实是&#xff1a;现场有二十多种设备&#xff0c;PLC、传感器、数控机床、冷库控制器、能耗采集器&#xff0c;每一种都有自己的通信方式。…

作者头像 李华