站内搜索

输入关键词开始搜索

Cesium 地形开挖:裁剪真实地表并重建坑壁坑底

文章目录

地形开挖案例不是修改 Cesium World Terrain 的原始瓦片。它先裁掉多边形内的地表,再根据真实边界高程生成坑壁和坑底,从视觉上形成可管理的开挖区域。

真实山地上的地形开挖场景与坑底、深度和方量参数面板

实现思路导图

flowchart TD
  ROOT[地形开挖]
  ROOT --> BOUNDARY[边界准备]
  BOUNDARY --> DRAW[绘制与方向统一]
  BOUNDARY --> SAMPLE[加密边界并采高]
  ROOT --> CLIP[Globe 裁剪平面]
  ROOT --> REBUILD[重建坑体]
  REBUILD --> WALL[侧壁网格]
  REBUILD --> BOTTOM[坑底函数与封口]
  ROOT --> MEASURE[面积与体积估算]
  ROOT --> EDIT[编辑后完整重建]

绘制只产生边界

用户左键添加顶点,右键结束。边界确认后,组合式函数进入异步创建流程:采样边界、计算坑底、建立裁剪集合、生成墙体与底面,最后再写入区域列表。

function startDraw() {
  if (!ready.value || busy.value || !drawTool) return;
  busy.value = true;
  drawing.value = true;
  drawTool.start();
}

busy 防止同一时间重复创建区域。它只解决当前页面内的并发,异步采样仍要使用操作编号或取消状态淘汰过期结果。

三种坑底策略

相对最低边界模式先取得边界最低高程,再向下减去开挖深度。绝对标高模式直接使用设计海拔。设计坡底模式还需要坡度百分比和方向,在局部 ENU 平面中计算每个位置的目标高度。

坑壁的上边缘来自真实地形采样,下边缘来自坑底函数。两条边界按相同采样顺序连接成三角带,才能避免墙面扭曲。

多区域共享裁剪管理器

Cesium Globe 的裁剪集合是场景级状态。每个区域直接覆盖 globe.clippingPolygons 会让后创建的区域顶掉旧区域。案例使用 TerrainRegionClippingManager 汇总所有区域,并在显隐、删除和编辑时统一重建集合。

clippingManager.add(id, clippingPositions, true);
clippingManager.setVisible(id, visible);
clippingManager.remove(id);

管理器内部保存所有区域。每次状态变化都会从当前可见区域重建统一的 ClippingPolygonCollection。

方量是采样估算

程序在开挖多边形内按指定间距采样原地形,比较原高程与目标坑底高程,并按网格面积累计体积。采样间距越小,结果越细,等待时间和请求量也越高。

这个数值适合交互预估。它受地形数据精度、边界离散和采样间距限制,不能直接替代测绘成果。

编辑与归档

顶点拖动、插入、删除使用独立编辑状态,并提供撤销、重做。方案导出保存边界与参数,不保存 Cesium Primitive;导入时重新采样和构建,才能适应当前地形和 Cesium 运行时。

实现主线是:裁剪原地表,边界采样决定坑口,坑底函数决定底面,再用统一管理器维持多区域状态。

截图中的几何分别是什么

截图中棕色水平面是坑底,四周竖直或倾斜的棕色面是坑壁,坑口之外仍由 Cesium World Terrain 渲染。左侧区域卡片已经出现面积、周长、坑底标高和估算挖方,说明截图来自创建完成状态。

这张图故意保留斜视角。俯视只能看到一块棕色矩形,无法说明原地形被裁掉后补了侧壁和底面。

裁剪与重建是两条管线

地形开挖不是一项操作,而是两条并行结果:

  1. ClippingPolygonCollection 让 Globe 不再绘制多边形内部地形。
  2. 自定义 Primitive 在空洞中补坑壁和坑底。

只做裁剪会看到地球内部或背景色;只画坑体又会被原地形深度遮住。两者使用同一边界,但资源生命周期不同,因此由 TerrainRegionClippingManager 和 TerrainExcavationPlan 分别管理。

边界为什么要重新采样

用户只画四个点时,真实山地边界在四条边之间仍有起伏。案例按“每边采样数”把测地边界细分,再调用 sampleTerrainMostDetailed 获取每个点的真实高程。

用户顶点 → 测地线插值 → 稠密边界点 → 地形采样

坑壁上边缘使用采样高程,下边缘使用坑底函数。两个数组索引一一对应,相邻索引组成两个三角形。若边界方向不统一,三角形绕序会翻转,背面剔除后出现缺口。

三种坑底函数具体怎么计算

相对最低边界:

bottom = min(boundaryHeights) - depth

绝对标高:

bottom(position) = configuredElevation

设计坡底先把位置转换到局部 ENU,沿指定方向计算水平投影距离:

bottom(x, y) = baseElevation + slope × projectedDistance(x, y)

因此坡底不是把整个坑体旋转,而是每个坑底顶点拥有不同目标高程。修改坡度方向时只需重建坑底与墙体,不必重新采样原地形。

方量估算怎样避免请求爆炸

程序在多边形包围盒内生成规则格点,过滤区域外点,采样原地形并计算挖深:

cellVolume = max(terrainHeight - bottomHeight, 0) × cellArea

区域过大时自动增大采样间距,把总点数限制在约 1600。面板展示实际采样点数,避免把自动降采样后的结果当成固定精度成果。

边界单元只按格点中心是否在多边形内计入,会带来离散误差。工程级体积需要对边界单元做面积裁剪,并使用更高精度地形或测量数据。

编辑时为什么要重建而不是移动墙面

拖动一个边界点会改变测地边界、采样高程、裁剪集合、坑壁拓扑、面积和体积。直接改四个可见角点无法保持中间采样点正确。因此编辑提交后重新走创建管线,编辑过程只显示轻量预览。

从零复现:地形开挖教程

案例源码位于 src/demos/cesiumdemos/terrain-excavation/。开挖由两条管线组成:Globe 裁剪负责把原地形挖空,自定义网格负责补回坑壁和坑底。

第 1 步:绘制并整理边界

双击完成多边形后,移除首尾重复点,计算有符号面积并统一为逆时针。边界方向会影响裁剪平面法线,方向错误会保留坑内、裁掉坑外。

if (signedArea(localPoints) < 0) localPoints.reverse();

先只显示边界线和顶点序号。确认没有自交,再进入裁剪阶段。

第 2 步:生成向内裁剪平面

对每条边取中点与切线,在局部 ENU 中求指向多边形内部的水平法线,再转换为世界坐标 ClippingPlane。把平面集合赋给 viewer.scene.globe.clippingPlanes。

完成标志:开启裁剪后,多边形内部原地形消失,外部完全保留。若结果相反,只调整统一的法线方向,不要逐边手工取反。

第 3 步:加密边界并采样原高程

按固定米距对每条边插入采样点,批量请求地形高程。坑壁顶部必须使用这些真实高度,否则山坡上的四角直墙会穿出或埋入地形。

保存 {position, topHeight, cumulativeDistance}。累计距离用于侧壁 UV,保证纹理沿整个边界连续。

第 4 步:计算坑底高度

固定深度模式可用 bottom = referenceHeight - depth;固定绝对高程直接使用目标值;倾斜模式在局部平面内定义一个高程函数。

const bottomHeight = baseHeight + slopeX * localX + slopeY * localY;

对山地开挖,参考高程应明确是边界最低值、平均值还是指定标高。不同定义会得到完全不同的方量。

第 5 步:构建侧壁网格

每个边界样本生成顶部和底部两个顶点,相邻样本连接成两个三角形。顶部使用采样高程,底部使用坑底函数,法线按三角面或平滑方式生成。

top[i] ---- top[i+1]
  |       /   |
bottom[i] -- bottom[i+1]

最后一段必须连接回第一个点。出现一条竖直裂缝时,通常是闭环索引或 UV 累计距离没有封口。

第 6 步:三角化坑底

把局部二维多边形送入三角化算法,得到索引;每个二维顶点再用坑底函数计算高度并转回世界坐标。坑底和侧壁共享边界底部点,避免浮点差异造成接缝。

第 7 步:估算面积与体积

在局部二维平面计算多边形面积。对内部规则采样点计算 max(terrainHeight-bottomHeight, 0),以采样单元面积加权求和。

这只是数值估算。教程中同时显示采样间距,读者才能判断方量精度;不应只给一个看似精确的小数。

第 8 步:编辑、删除和恢复

编辑完成后销毁旧侧壁、坑底和裁剪平面,再从新边界完整重建。删除最后一个开挖区时恢复 Globe 原裁剪状态,不能把其他功能已有的 ClippingPlaneCollection 一并覆盖。

验收与排错

  • 裁掉了多边形外部:检查顶点方向与法线。
  • 坑壁顶部悬空:提高边界采样密度并使用真实地形高程。
  • 坑底出现交叉三角形:先处理自交多边形,再执行三角化。
  • 删除区域后地形仍缺失:检查裁剪集合是否真正从 Globe 移除并销毁。

相关文章

Cesium 三维风场:用 WebGL 2 GPU 粒子描绘全球气流

拆解案例中的风场数据、GPU 粒子更新、轨迹渲染、颜色映射和 Vue 参数控制

Cesium 北斗三维网格:在城市 3D Tiles 上做 GeoSOT-3D 剖分

介绍区域体素生成、建筑表面采样、结构碰撞检测、Worker 几何构建和网格编码查询