最近完成了一个基于 Cesium + JavaScript + Web Worker + GPU Shader 的二维水动力实时模拟原型,并且已经在真实地形场景中完整跑通。
这个项目最开始只是希望在 Cesium 三维场景中实现类似洪水演进的动态效果,但随着功能逐步完善,最终已经从最初的“模拟峡谷 + 固定上游来水”,扩展到了:
真实地形选取 → DEM 高程采样 → 二维水动力网格 → 多入流/多出流 → Q(t) 洪水过程线 → P(t) 降雨过程线 → 空间分区降雨 → 入渗/产流 → 坡面汇流 → 河道洪水 → GPU 实时水面渲染。
目前同时接入了 天地图影像与中文注记,用于真实地理场景展示。
本文记录一下整个系统的技术路线、关键实现以及可以进一步落地的应用场景。
一、为什么要做这个系统?
传统 WebGIS 中的洪水展示,大量项目采用的是:
-
已计算好的淹没面加载;
-
GeoJSON 动态播放;
-
热力图;
-
水面 Polygon 高程变化;
-
预生成洪水动画。
这类方法非常适合“结果展示”,但有一个明显的问题:
地图本身并没有真正进行水动力计算。
如果改变上游流量、降雨、地形或者边界条件,通常需要重新调用后端模型计算,然后再把结果传回前端。
我这次想尝试的是另一条路线:
能不能直接在浏览器中,让真实 DEM、降雨和边界条件参与二维水动力计算,然后实时把计算结果映射到 Cesium 三维场景?
因此整个系统的核心目标并不是单纯“把水做得好看”,而是建立一条完整的数据链。
真实地图
↓
Cesium World Terrain
↓
用户框选计算区域
↓
DEM 高程采样
↓
二维水动力网格
↓
Q(t) / P(t) / 入渗产流
↓
Web Worker 求解
↓
h / u / v
↓
GPU Shader
↓
Cesium 三维洪水动态显示
二、真实地形:从模拟峡谷切换到实际 DEM
早期版本为了验证水动力和渲染流程,地形是通过程序生成的峡谷 DEM。
这种方式适合算法调试,但很快就遇到了问题:
模拟峡谷能够说明程序“能跑”,却不能说明它能够用于真实区域。
因此后续版本将地形源改成了真实 DEM。
现在的工作流程是:
-
加载 Cesium 三维场景;
-
用户在地图中框选目标区域;
-
得到框选区域的经纬度 Bounds;
-
在区域内部建立规则采样点;
-
从 Cesium Terrain 获取真实高程;
-
对 DEM 进行必要预处理;
-
重采样成二维水动力求解网格;
-
根据新的 DEM 初始化水动力模型。
这一步很重要,因为从这里开始:
水流方向不再由程序预设的“峡谷形状”决定,而真正受到当地地形高程控制。
为了提高实际使用体验,DEM 获取也采用了分批采样,而不是一次请求几十万个高程点。
三、天地图 + Cesium World Terrain
系统中现在实际上使用了两套不同的数据源:
天地图
→ 负责影像底图与中文地名注记
Cesium World Terrain
→ 负责真实地形高程
天地图接入:
-
img_w:卫星影像; -
cia_w:影像中文注记。
这样做的好处是:
视觉上使用国内更熟悉的影像与地名数据,而二维水动力需要的高程仍然独立来自 Terrain。
天地图 Key 和 Cesium Ion Token 都没有硬编码到源码中,而是通过界面配置。
四、真正参与计算的二维水动力模型
水动力部分没有运行在 Cesium Shader 内,而是在独立的:
solver.worker.js
中执行。
使用 Web Worker 的主要原因是:
如果把二维网格数值计算直接放在浏览器主线程,Cesium 相机操作、UI 和动画很容易出现明显卡顿。
因此整个程序分为两个线程。
主线程
Cesium / UI / GPU Rendering
↑
│ h,u,v
↓
Web Worker
2D Hydrodynamic Solver
目前求解器采用的是 local-inertial / shallow-water approximation 思路。
主要计算量包括:
-
地形高程
z; -
水深
h; -
X、Y 方向面流量;
-
水面高程
z + h; -
重力坡降;
-
Manning 阻力;
-
自适应时间步长;
-
Wet/Dry 湿干处理;
-
边界入流;
-
自由出流;
-
降雨有效产流;
-
质量守恒统计;
-
二维速度
u / v恢复。
因此屏幕上看到的洪水不是一张沿河道移动的纹理。
每一个时刻的水深和速度都是根据当前 DEM、边界和降雨重新计算出来的。
五、从固定“北进南出”到任意河流方向
真实地形接入后,很快又出现了一个问题。
早期模型默认:
北侧 → 入流
南侧 → 出流
这对于程序生成的南北向峡谷没有问题。
但是到了真实区域,一条河可能是:
西 → 东
东 → 西
南 → 北
西 → 南
多个支流 → 一个主河道
因此后续将边界系统完全重构。
现在可以直接在地图上:
点击添加入流边界
以及:
点击添加出流边界
程序会自动将鼠标位置吸附到距离最近的计算域边界。
每一个边界都保存:
边界方向
+
沿边界的位置
+
作用宽度
没有配置的边界默认保持封闭。
因此模拟区域不再要求特定朝向。
六、多个入流和多个出流
为了模拟真实流域,仅有一个入口同样不够。
例如:
支流 A
↓
支流 B → 主河道 → 出口 1
↓
支流 C
↓
出口 2
因此当前系统支持:
inlets[]
outlets[]
每个入流口可以单独设置:
流量 Q
边界宽度
位置
多个入流会同时参与水动力计算。
这意味着已经可以模拟:
-
两条支流汇流;
-
多条山沟同时来水;
-
主河道来水 + 支流来水;
-
多出口排洪;
-
分洪口。
七、Q(t):从固定流量升级为洪水过程线
真实洪水显然不会一直保持固定流量。
典型洪水过程通常经历:
基础流量
↓
快速上涨
↓
洪峰
↓
逐渐退水
因此系统继续加入了 Q(t) 流量过程线。
每个入口都可以独立导入 CSV:
time_s,flow_m3s
0,5
300,12
600,35
900,80
1200,120
1500,95
1800,60
2400,25
3000,10
3600,0
每一条支流都可以拥有不同的洪水过程。
例如:
支流 A → Q1(t)
支流 B → Q2(t)
支流 C → 固定 15 m³/s
过程线节点之间采用线性插值。
更重要的是,在一个数值时间步跨越过程线节点时,并不是简单读取某一个节点的 Q,而是对:
[t, t + dt]
范围内的分段线性过程线进行积分。
因此能够更准确地保持累计入流体积。
八、从“上游给水”进一步升级到“天空下雨”
只有 Q(t),本质上仍然是在告诉模型:
河流入口现在来了多少水。
但真实的山洪、流域洪水和城市内涝还有另一条非常重要的数据链:
降雨
↓
入渗
↓
产流
↓
坡面汇流
↓
河道汇流
↓
洪峰
因此 V8 增加了 P(t) 降雨过程线。
CSV 格式类似:
time_s,rain_mm_h
0,0
300,12
600,35
900,80
1200,120
1500,90
1800,55
2400,20
3000,5
3600,0
单位为:
mm/h
同样支持节点间线性变化和时间步积分。
这意味着现在可以模拟一场完整的设计暴雨过程,而不是只能设置:
降雨 = 50 mm/h
然后从头下到尾。
九、空间分区降雨
真实暴雨通常并不是整个区域完全一致。
例如山洪场景可能出现:
流域西北部:120 mm/h
流域中部:70 mm/h
流域下游:25 mm/h
所以系统进一步增加了地图框选降雨分区。
用户可以:
-
在地图选择一个矩形区域;
-
为该区域设置自己的降雨;
-
导入独立 P(t);
-
设置独立入渗参数。
而且支持多个区域覆盖。
采用的优先规则是:
后添加的小区域优先。
因此可以先设置一个大范围背景降雨,再设置局部暴雨中心。
例如:
全流域背景雨:20 mm/h
局部区域 A:
P1(t),峰值 80 mm/h
局部区域 B:
P2(t),峰值 150 mm/h
这样比全域统一降雨更接近真实暴雨空间结构。
十、降雨并不等于全部变成洪水
这是 V8 中一个比较关键的变化。
如果直接使用:
降雨量 = 地表产流量
那么结果通常会明显偏大。
因为真实降雨之后,一部分水会:
-
入渗;
-
被植被截留;
-
洼地蓄积;
-
蒸发;
-
产生其他损失。
当前版本首先加入了三种简化模型。
1. 无损失
有效产流 = 降雨
适用于:
-
算法测试;
-
完全不透水区域;
-
极端简化场景。
2. 径流系数法
有效产流 = C × P
如果:
C = 0.6
那么 100 mm 的降雨中按照模型简化为约 60 mm 形成有效产流。
这种方法虽然简单,但是对于数据不足的快速场景非常实用。
3. Horton 入渗
Horton 模型中,入渗能力随持续降雨逐步降低:
f(t) = fc + (f0 - fc)e^(-kt)
其中:
f0 = 初始入渗能力
fc = 稳定入渗能力
k = 衰减系数
系统另外增加了:
recovery
用于模拟停止降雨之后土壤入渗能力逐渐恢复。
同时可以设置:
impervious
即不透水面积比例。
因此,同样一场暴雨作用于两个不同区域,可以产生完全不同的径流响应。
十一、V8 已经支持纯降雨驱动
这里是一个我觉得比较有意思的变化。
早期模型一定要存在:
上游入流
+
下游出流
否则没有水可以进入模型。
现在则完全不同。
可以直接运行:
P(t)
+
无河流入口
+
无河流出口
也就是说:
水直接来自天空。
因此可以模拟封闭低洼地区持续降雨后的积水。
也可以:
P(t)
+
一个或多个出口
用于坡面汇流。
甚至可以:
Q1(t)
+
Q2(t)
+
多个 P(t)
+
Horton
+
多个 Outlet
模拟较复杂的流域洪水过程。
十二、质量守恒是一个非常重要的检查指标
做这种实时水动力 Demo 时,很容易只关注:
水有没有流起来?
但数值模型真正需要持续关注的是:
水从哪里来,又去了哪里?
因此 Worker 会累计统计:
边界入流
+
局部水源
+
降雨有效产流
-
边界出流
并与当前网格水量进行对比。
降雨部分还会分别记录:
累计总降雨
累计入渗
累计有效产流
理论上满足:
总降雨
≈
入渗/损失
+
有效产流
而水动力网格满足:
当前水量
≈
初始水量
+
边界入流
+
有效产流
+
其他水源
-
出流
开发过程中这个指标非常有价值。
很多看起来像“渲染问题”的异常,最终其实是:
-
Wet/Dry 处理丢水;
-
边界通量符号错误;
-
时间积分错误;
-
降雨量单位换算错误。
十三、GPU 连续水面,而不是一个格子一个 Primitive
数值求解使用规则网格,但如果直接把每个网格画成 Cesium Polygon,网格一多性能会快速下降,而且水面会产生明显的“马赛克”。
因此当前渲染采用:
一个连续 glTF Water Mesh
+
Cesium CustomShader
+
动态状态 Texture
求解器返回:
h
u
v
即:
水深
X 方向流速
Y 方向流速
然后编码到 GPU 状态纹理。
Shader 中完成:
-
水深颜色;
-
流速颜色;
-
水面透明度;
-
Wet/Dry 显示;
-
动态高光;
-
流向箭头;
-
时间插值。
水体网格的基础高程直接从 Solver DEM 双线性采样,因此水面和水动力计算使用同一套高程基准,可以减少水面“悬空”和穿地问题。
十四、为什么水面动画比较平滑?
水动力求解器并不需要按照显示器 60 FPS 的频率运行。
Worker 负责计算物理状态。
浏览器:
requestAnimationFrame
负责视觉刷新。
主线程保留相邻计算状态:
State A
State B
Shader 再通过插值:
mix(A, B, t)
得到显示时刻的状态。
所以实现了:
求解频率和显示帧率解耦。
这是浏览器实时数值模拟中非常有用的一种结构。
十五、目前可以应用在哪些场景?
虽然当前模型仍然属于研究/演示原型,但从系统架构来看,已经可以覆盖不少应用方向。
1. 山洪灾害模拟
真实山区 DEM + 暴雨 P(t) + Horton 入渗,可以观察:
-
山谷汇流;
-
沟道涨水;
-
洪峰传播;
-
下游受影响区域。
后续如果进一步加入土地利用和土壤参数,这个方向会更有意义。
2. 中小流域洪水演进
配置:
多支流 Q(t)
+
流域降雨 P(t)
+
多个出口
可以用于展示不同支流洪峰叠加后对主河道的影响。
3. 城市内涝快速演示
如果有高精度城市 DEM,可以采用:
纯降雨 P(t)
+
高不透水率
+
道路/排水出口
观察低洼区域积水过程。
当然,要真正用于城市内涝工程计算,还需要进一步加入:
-
建筑物阻水;
-
雨水管网;
-
泵站;
-
雨水口;
-
更精细 DEM。
4. 水库泄洪和下游淹没演示
可以将泄洪过程作为:
Q(t)
输入模型。
然后观察不同泄洪过程对下游水深和流速的影响。
5. 堤坝溃决快速情景推演
可以把溃口看成一个随时间快速变化的 Q(t) 边界。
用于演示:
溃口形成
→
流量快速增加
→
洪水向下游传播
如果进一步加入动态溃口宽度模型,可以继续深化。
6. 应急指挥三维可视化
Cesium 最大的优势之一,是模拟结果天然处于真实三维地理空间。
后续可以叠加:
-
道路;
-
村庄;
-
医院;
-
学校;
-
避难场所;
-
人口;
-
摄像头;
-
水位站;
-
风险点。
最终做成:
洪水模拟
+
GIS
+
应急资源
+
风险分析
7. 防洪方案比选
可以建立多个方案:
方案 A:现状
方案 B:增加排洪口
方案 C:改变出口宽度
方案 D:降低洪峰
然后比较:
-
最大水深;
-
最大流速;
-
淹没范围;
-
退水时间。
8. 水动力教学与算法验证
由于整个系统运行在浏览器中,并且地形、边界、降雨都可以交互配置,因此也非常适合用于:
-
水动力教学;
-
WebGPU/WebGL 数值可视化研究;
-
GIS + 数值模型耦合研究;
-
水文水动力算法原型验证。
十六、当前版本仍然有哪些不足?
这一点必须说明。
目前系统是:
演示/研究原型级二维水动力模型。
并不是经过工程认证、率定和验证的专业洪水预测软件。
目前仍需要继续解决:
-
DEM 水文校正;
-
河道真实断面;
-
土地利用空间 Manning n;
-
土壤空间参数;
-
SCS-CN / Green-Ampt;
-
建筑物阻水;
-
城市排水管网;
-
雷达降雨时空栅格;
-
实测雨量站/水位站率定;
-
与 HEC-RAS、MIKE、InfoWorks 等成熟模型结果对比验证。
因此目前更适合定位为:
Web 端实时水动力技术原型 / 数字孪生可视化底座 / 洪水情景推演 Demo。
而不是直接用于工程设计结论。
十七、后续准备继续做什么?
完成 V8 后,下一步技术路线其实已经比较明确。
我希望继续把“手工参数”逐步替换成真实 GIS 数据:
土地利用 GeoJSON / Raster
↓
空间 Manning n
土壤数据
↓
空间入渗参数
雷达降雨
↓
P(x,y,t)
河网 / 建筑物
↓
真实阻水与汇流结构
进一步可以加入:
-
SCS-CN;
-
Green-Ampt;
-
GeoTIFF / COG;
-
雷达降雨时序;
-
土地利用;
-
土壤类型;
-
空间 Manning;
-
建筑物;
-
道路;
-
河网;
-
实时雨量站;
-
水位站;
-
IoT 数据。
最终希望形成的是:
真实 GIS 数据
+
实时水文数据
+
二维水动力模型
+
Cesium 三维地理场景
+
GPU 实时渲染
也就是一个真正的数据驱动型流域洪水数字孪生原型。
结语
这个项目从最开始的程序生成峡谷,逐渐走到了真实 DEM、多边界、Q(t)、P(t)、降雨产流和三维动态显示。
过程中最大的体会是:
WebGIS 不一定只能“展示”水动力模型的结果,也可以逐渐承担模型交互、实时计算和结果表达的一整套工作。
Cesium 负责真实地理空间,Web Worker 负责数值求解,GPU Shader 负责高性能连续水面显示。
三者结合之后,Web 端洪水模拟的可玩空间其实非常大。
目前 V8 已经完整跑通并完成录屏。
后续还会继续往 GIS 数据驱动、雷达降雨、空间参数以及更真实的水文产流模型方向完善。
如果大家对 Cesium、水动力、WebGIS、洪水模拟、数字孪生 这几个方向感兴趣,欢迎交流。


1822

被折叠的 条评论
为什么被折叠?



