1. 为什么需要凸多边形裁剪在三维地理信息系统中我们经常需要对地形进行局部展示或隐藏。比如你想突出显示某个工业园区的地形起伏或者需要隐藏军事禁区等敏感区域。这时候就需要用到地形裁剪技术。Cesium作为主流的三维地球可视化引擎提供了ClippingPlane裁剪平面功能来实现这一需求。但原生API使用起来比较底层需要开发者自行处理很多几何计算。我在实际项目中就遇到过这样的需求客户需要在三维场景中动态划定一个区域选择保留区域内或区域外的地形。这里有个关键限制目前只能处理凸多边形。什么是凸多边形简单说就是没有凹陷的多边形比如三角形、矩形、五角星都是凸多边形。而像凹字形、回字形就是凹多边形。这个限制主要是因为凹多边形的裁剪需要更复杂的算法而凸多边形可以通过简单的平面切割实现。2. 挖除与挖出的实战场景2.1 挖除模式保留多边形外部挖除模式typefalse会保留多边形外部的区域把多边形内部挖空。这种模式特别适合以下场景隐藏敏感区域比如军事基地、私人领地展示环形区域比如只显示城市环线以外的区域地形对比挖掉一部分地形来观察地下结构// 使用挖除模式 areaClipping(pointList, false)2.2 挖出模式保留多边形内部挖出模式typetrue则相反会保留多边形内部的区域。典型应用场景包括重点区域展示突出显示某个工业园区或开发区地形剖面分析只显示特定区域的地形数据教学演示聚焦某个地理区域进行讲解// 使用挖出模式 areaClipping(pointList, true)我在一个智慧城市项目中就用到这个功能。客户需要在三维场景中框选建筑群只显示选定区域内的地形和建筑方便进行局部规划分析。通过挖出模式完美实现了这个需求。3. 核心算法原理解析3.1 法向量计算的关键步骤裁剪的核心是计算每个边的裁剪平面。这里的关键是求出平面的法向量normal。算法步骤如下计算两个相邻点的中点获取从地心指向中点的向量up向量计算从中点指向下一个点的向量right向量通过up和right的叉积得到法向量const midpoint Cesium.Cartesian3.add(points[i], points[nextIndex], new Cesium.Cartesian3()) const up Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3()) const right Cesium.Cartesian3.subtract(points[nextIndex], midpoint, new Cesium.Cartesian3()) const normal Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3())3.2 点序方向自动校正一个容易踩坑的地方是点集的顺序。我发现当点集是顺时针时挖出和挖除的效果会相反。所以代码中加入了自动校正逻辑if (sum 0 type) { // 逆时针 points.reverse() } else if (sum 0 !type) { // 顺时针 points.reverse() }这个判断很关键它通过计算叉积的z分量和来确定点序方向确保最终效果符合预期。4. 工程化封装实践4.1 函数参数设计为了让工具更易用我将核心功能封装成一个函数只暴露必要的参数/** * 区域裁剪函数 * param {Array} points - 点坐标数组格式[{x,y,z}...] * param {boolean} [typefalse] - 裁剪类型false挖除/true挖出 */ function areaClipping(points, type false) { // 实现代码... }这种设计让调用变得非常简单开发者只需要准备点集和选择模式即可。4.2 性能优化建议在实际使用中我总结了几点优化经验尽量减少动态裁剪的次数可以先用小数据量测试效果对于静态区域可以缓存ClippingPlaneCollection对象当不需要裁剪时记得将clippingPlanes设为null释放资源// 禁用裁剪 viewer.scene.globe.clippingPlanes null5. 凸多边形限制的突破思路虽然当前只支持凸多边形但通过一些技巧可以实现近似凹多边形的效果凸多边形分解法将凹多边形分解为多个凸多边形组合多裁剪面叠加使用多个裁剪平面共同作用后期处理方案结合Cesium的PostProcessStage实现更复杂的裁剪我在一个水利项目中就采用第一种方法将复杂的流域区域分解为多个凸多边形进行组合裁剪效果相当不错。6. 完整代码实现以下是经过实战检验的完整实现包含了详细的错误处理和边界条件判断import * as Cesium from cesium export function areaClipping(points, type false) { if (!points || points.length 3) { console.error(至少需要3个点才能构成多边形) return } try { const pointsCoor points.map(({x,y,z}) new Cesium.Cartesian3(x,y,z)) // 方向自动校正 let sum 0 for (let i 0; i pointsCoor.length; i) { const pointA pointsCoor[i] const pointB pointsCoor[(i1)%pointsCoor.length] const crossProduct Cesium.Cartesian3.cross(pointA, pointB, new Cesium.Cartesian3()) sum crossProduct.z } if ((sum 0 type) || (sum 0 !type)) { pointsCoor.reverse() } // 创建裁剪平面 const clippingPlanes [] for (let i 0; i pointsCoor.length; i) { const nextIndex (i 1) % pointsCoor.length const midpoint Cesium.Cartesian3.add(pointsCoor[i], pointsCoor[nextIndex], new Cesium.Cartesian3()) Cesium.Cartesian3.multiplyByScalar(midpoint, 0.5, midpoint) const up Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3()) const right Cesium.Cartesian3.subtract(pointsCoor[nextIndex], midpoint, new Cesium.Cartesian3()) Cesium.Cartesian3.normalize(right, right) const normal Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3()) Cesium.Cartesian3.normalize(normal, normal) const originCenteredPlane new Cesium.Plane(normal, 0.0) const distance Cesium.Plane.getPointDistance(originCenteredPlane, midpoint) clippingPlanes.push(new Cesium.ClippingPlane(normal, distance)) } viewer.scene.globe.clippingPlanes new Cesium.ClippingPlaneCollection({ planes: clippingPlanes, enabled: true, unionClippingRegions: type, edgeColor: Cesium.Color.YELLOW, edgeWidth: 1.0 }) } catch (error) { console.error(裁剪功能出错:, error) } }这个实现已经处理了各种边界情况包括点数不足的校验异常捕获内存管理视觉效果优化7. 常见问题排查在实际应用中可能会遇到以下问题裁剪效果相反检查点集顺序和type参数是否匹配边缘锯齿明显尝试调整edgeWidth参数或开启抗锯齿性能下降检查点集数量过多点会导致计算量激增裁剪面不闭合确保点集构成闭合多边形首尾点要相同遇到问题时建议先用简单的三角形测试逐步增加复杂度。我在开发过程中就曾因为一个点序问题调试了半天最后发现是客户提供的GeoJSON坐标顺序不一致导致的。