CesiumJS 地下可视化实战:关闭一行碰撞检测,让相机钻进地下并挂上线网
【免费下载链接】cesiumAn open-source JavaScript library for world-class 3D globes and maps :earth_americas:项目地址: https://gitcode.com/GitHub_Trending/ce/cesium
本文以 CesiumJS 开源库为基础,从一个市政单位的具体需求出发讲解 CesiumJS 地下可视化:某城市管理中心需要把一条 10 公里长的地下管线展示给公众,要求浏览器直接打开,不接受桌面客户端。如果你有前端基础但没接触过 Cesium,本文按“相机为什么默认进不了地下 → 如何让地面可见 → 如何放大地下尺度 → 管线数据怎么挂”的顺序推进,每一步都对应仓库里可复现的示例。
相机碰撞检测仍然开着:为什么你默认进不了地下
先说一个最常见的卡点:很多人第一次尝试时,把实体放在负高度上,却发现相机飞不过去,或者飞进去以后看到的只是一片黑地。
原因在相机控制器上。Cesium 默认的屏幕空间相机控制器开启了碰撞检测,scene.screenSpaceCameraController.enableCollisionDetection默认为true:当地形位于相机与目标位置之间时,相机会被挡回地形表面。而且地形是闭合的椭球面,法线全部朝外,相机一旦穿过地面,四面八方看到的地形背面默认渲染为黑色。
第一步就是关掉这个保护,给相机进入地下的权限:
const viewer = new Cesium.Viewer('cesiumContainer'); viewer.scene.screenSpaceCameraController.enableCollisionDetection = false; const pos = Cesium.Cartesian3.fromDegrees(116.4, 39.9, -500); viewer.entities.add({ position: pos, point: { pixelSize: 10, color: Cesium.Color.RED } }); viewer.scene.camera.flyTo({ destination: pos, offset: new Cesium.HeadingPitchRange(0, -0.5, 2000) });以上是最小可运行示例:创建 Viewer、关闭碰撞检测、在地面以下 500 米处放一个红点、让相机降下去观察它。fromDegrees第三个参数是高度,负值即地下;flyTo的offset用航向、俯仰、距离描述,俯仰取负值时相机朝下看。
三维地球场景的整体形态可以参考下面这张示例截图,实体可以同时分布在地表以上和以下:
让地面“透”起来:translucency 与地下底色两个开关
进入地下后你会发现画面只有两种:黑屏,或者看到地形背面。这是地形作为闭合面片的正常行为,Cesium 在Globe上提供了两个开关来处理。
globe.translucency:在地面开一扇窗
globe.translucency.enabled = true后,地形正面片元变为半透明材质,frontFaceAlpha控制透明度,取 0 到 1 之间的值,例如 0.5。两个进阶用法:frontFaceAlphaByDistance是一个NearFarScalar,可以按相机与地面的距离让地面逐渐变透明,从地表往下看和进入地下时的过渡会更平滑;rectangle可以把半透明范围限制在指定区域,避免全局“玻璃地球”影响正常浏览。Sandcastle 示例集 中的 globe-translucency 示例展示了按距离淡出和限定矩形两种模式,globe-interior 示例则把地幔和地核做成几层椭ipsoid、从地表以下向内观察,是“看穿地表”的典型演示。
globe.undergroundColor:地下的“天花板颜色”
当相机位于地形表面之下时,globe.undergroundColor生效:原本被地形挡住的视线会渲染成这个颜色,而不是纯黑。配合undergroundColorAlphaByDistance做距离淡出,地下空间的明暗过渡更自然,仓库里的 underground-color 示例就是这一套参数的完整演示。还有一个细节:如果想真正“看到地球内部”,showGroundAtmosphere建议一并关闭,否则大气散射效果仍会绘制在地下画面上,颜色发灰。
垂直夸大:把 5 米深的管线放到城市尺度能看见
第二个真正卡住项目的地方是尺度。城市管线埋深通常只有几米到十几米,在地球尺度下远小于地形瓦片自身的高程起伏,坐标再精确,线条也会被地面“吃掉”。
Cesium 的解法是scene.verticalExaggeration,例如取 2.0,把高差整体放大;verticalExaggerationRelativeHeight指定放大所参考的高度面,参考面以上抬高、以下压深,参考面本身不漂移。注意放大后地形自己也会起伏,所有clampToGround的实体会跟着动。terrain-exaggeration 示例用高程带材质把参考高度直接画在地面上,“先让参考面可见,再放业务对象”这个调试顺序值得照搬。
城市级三维模型下同一道理:相机近裁剪面、地形细节层级和三维瓦片加载范围都合理时,地下层才能稳定渲染,而不是忽隐忽现:
从单点到线网:管线数据与点击查询
画面就绪后,把数据放进来。对中等规模管线网(几千个要素以内),用实体就够,不必写自定义 primitive:主管线用polyline实体,positions按巡线点经纬度和负高度给出,地下管线不建议clampToGround,直接给负高程;阀门、井位等点状要素用point或billboard,后者可以用PinBuilder生成的图钉纹理,宽度、高度、颜色都可调:
点击查询方面,在scene.canvas上注册LEFT_CLICK事件,用scene.pick取到光标下的实体,再从它的属性里读管线名称、埋深、材质,这部分和普通实体开发没有区别。
踩坑速查:动手前自检表
| 现象 | 最可能原因 | 处理方式 |
|---|---|---|
| 相机进不了地面以下 | 碰撞检测开着 | enableCollisionDetection = false |
| 进地下后黑屏 | 未设置地下底色 | globe.undergroundColor |
| 从地表往下看穿透混乱 | 地形正面无半透明 | globe.translucency配合距离淡出 |
| 管线看不见或被地面吃掉 | 埋深远小于瓦片起伏 | verticalExaggeration加大并指定参考高度 |
| 地下画面发灰、发雾 | 地面大气未关 | globe.showGroundAtmosphere = false |
| 3D Tiles 表面仍挡住相机 | 瓦片集自带碰撞开关 | 检查Cesium3DTileset的enableCollision |
动手前再核对四件事:关闭碰撞检测只适合地下可视化这类应用,常规空中漫游请保持开启,否则相机会钻进地形和建筑;frontFaceAlpha只作用于地形正面片元,不影响三维瓦片和模型,模型想要“玻璃化”要在材质层处理;垂直夸大影响所有与高度相关的渲染,改完参数要重查贴地对象和阴影;半透明地形相比不透明地形有额外渲染开销,相机贴近地面时更明显,场景大就用translucency.rectangle限制范围。完整示例代码都在 Sandcastle 示例集 里,相关源码集中在 packages/engine/Source/Scene/,对照源码读比读文档快。
【免费下载链接】cesiumAn open-source JavaScript library for world-class 3D globes and maps :earth_americas:项目地址: https://gitcode.com/GitHub_Trending/ce/cesium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考