写在前面
前序章节掌握 3D Tiles 基础加载、Cesium3DTileStyle属性着色,仅能完成基础可视化;工业 BIM、倾斜摄影数字孪生项目常需要剖切查看内部结构、自定义动态光影特效、大场景流畅加载、显存防崩溃管控。 本章讲解五大生产级核心能力:ClippingPlaneCollection模型剖切、CustomShaderGPU 自定义着色器、debug 瓦片调试工具、skipLevelOfDetail跳级加速加载、maximumMemoryUsage内存管控,配套多特性组合综合案例,覆盖建筑剖面、动态热力、大场景性能优化业务需求。
本章学习目标
- 掌握
ClippingPlane单 / 多平面剖切,区分并集 / 交集裁剪模式,实现滑块动态剖切交互 - 理解
CustomShader顶点 / 片元着色器、uniform 传参、GLSL 基础语法,实现高度热力、脉冲发光特效 - 熟练使用全套
debugShow调试工具,快速定位瓦片缺失、层级切换、加载异常问题 - 掌握跳级加载策略,区分 REPLACE/ADD 瓦片细化规则,适配鸟瞰 / 室内漫游不同场景
- 配置显存内存上限,监听瓦片全生命周期事件,实现内存监控,解决大模型 OOM 崩溃问题
一、ClippingPlaneCollection 模型剖切平面
1.1 核心原理
裁剪平面由法线向量+距离值定义:法线指向保留渲染一侧,平面将三维空间分割两部分,另一侧模型直接剔除不渲染;多平面通过集合统一管理,支持两种裁剪运算模式。
- ClippingPlane:单剖切刀,法线 + distance 定义切割位置
- ClippingPlaneCollection:剖切平面组,统一控制切面轮廓、开关、运算逻辑
- unionClippingRegions:true = 并集(任意平面裁掉即隐藏),false = 交集(多平面重叠区域才隐藏)
1.2 基础配置参数
const clipPlanes = new Cesium.ClippingPlaneCollection({ planes: [ // 法线向下,高度50米处水平切割 new Cesium.ClippingPlane(new Cesium.Cartesian3(0,0,-1), 50) ], unionClippingRegions: true, edgeColor: Cesium.Color.CYAN, // 切面描边颜色 edgeWidth: 2, // 切面线宽 enabled: true }); tileset.clippingPlanes = clipPlanes;1.3 常用实战案例
案例 1:水平剖切,切除建筑上部露出地基
const clipPlanes = new Cesium.ClippingPlaneCollection({ planes: [new Cesium.ClippingPlane(new Cesium.Cartesian3(0,0,-1), 50)], edgeColor: Cesium.Color.CYAN, edgeWidth:2 }); tileset.clippingPlanes = clipPlanes;案例 2:双平面对角剖切(交集模式切角落)
const multiClip = new Cesium.ClippingPlaneCollection({ planes: [ new Cesium.ClippingPlane(new Cesium.Cartesian3(1,0,0),0), new Cesium.ClippingPlane(new Cesium.Cartesian3(0,1,0),0) ], unionClippingRegions: false, // 交集模式 edgeColor: Cesium.Color.YELLOW }); tileset.clippingPlanes = multiClip;案例 3:滑块动态调节剖切高度
<input id="clipSlider" type="range" min="0" max="200" value="100">const clipPlanes = new Cesium.ClippingPlaneCollection({ planes: [new Cesium.ClippingPlane(new Cesium.Cartesian3(0,0,-1), 100)], edgeColor: Cesium.Color.RED }); tileset.clippingPlanes = clipPlanes; document.getElementById("clipSlider").oninput = e=>{ clipPlanes.get(0).distance = parseFloat(e.target.value); viewer.scene.requestRender(); }1.4 两种裁剪模式对比
| 参数 | unionClippingRegions:true 并集 | unionClippingRegions:false 交集 |
|---|---|---|
| 逻辑 | 任一平面剔除则隐藏 | 仅多平面重叠区域剔除 |
| 效果 | 平面越多,剔除范围越大 | 平面越多,剔除范围越小 |
| 适用 | 顶部、侧面多面同时剖切 | 只切除建筑角落一小块 |
二、CustomShader 自定义 GPU 着色器
2.1 适用场景
基础Cesium3DTileStyle仅支持简单静态着色,无法实现:高度渐变热力、脉冲呼吸发光、实时数据映射、菲涅尔边缘光;CustomShader 直接编写 GLSL 片元 / 顶点着色代码,在 GPU 并行运算,实现高级视觉特效。
2.2 基础结构说明
- uniforms:JS 向 GLSL 传递全局变量(颜色、时间、阈值)
- vertexShaderText:顶点着色,修改模型几何顶点位置
- fragmentShaderText:片元着色,修改每个像素最终颜色(最常用)
- mode:MODIFY_MATERIAL(基于原始材质修改,推荐)/REPLACE_MATERIAL(完全覆盖原有纹理)
2.3 内置核心变量
| 变量 | 作用 |
|---|---|
| fsInput.attributes.positionMC | 模型局部坐标 Z 值,实现按高度着色 |
| fsInput.attributes.normalMC | 顶点法线,制作边缘高光 |
| material.diffuse | 模型基础漫反射颜色 |
| material.emissive | 自发光,不受太阳光影响 |
2.4 实战 1:建筑高度热力渐变着色
const heatShader = new Cesium.CustomShader({ uniforms:{ u_minH:{type:Cesium.UniformType.FLOAT,value:10}, u_maxH:{type:Cesium.UniformType.FLOAT,value:300} }, fragmentShaderText:` void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material){ float h = fsInput.attributes.positionMC.z; float t = clamp((h-u_minH)/(u_maxH-u_minH),0.0,1.0); vec3 color = mix(vec3(0,0,1),vec3(1,0,0),t); material.diffuse = color; material.emissive = color*0.2; } ` }); tileset.customShader = heatShader;2.5 实战 2:选中建筑金色脉冲呼吸效果
const pulseShader = new Cesium.CustomShader({ uniforms:{ u_time:{type:Cesium.UniformType.FLOAT,value:0}, u_selected:{type:Cesium.UniformType.BOOL,value:false} }, fragmentShaderText:` void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material){ if(u_selected){ float pulse = 0.6 + 0.4*sin(u_time*5.0); vec3 gold = vec3(1,0.84,0); material.diffuse = mix(material.diffuse,gold,pulse*0.5); material.emissive = gold*pulse*0.6; } } ` }); tileset.customShader = pulseShader; // 每帧更新时间,驱动动画 viewer.scene.preRender.addEventListener(()=>{ pulseShader.setUniform("u_time", performance.now()/1000); })2.6 mode 模式选择建议
- MODIFY_MATERIAL:保留原始纹理、PBR 光照,仅叠加特效,性能开销低,绝大多数场景首选
- REPLACE_MATERIAL:完全丢弃原始贴图,自定义纯色 / 线框 / X 光效果,适合分析类场景
三、debugShow 瓦片调试工具集
3.1 六大调试开关,快速定位渲染异常
// 瓦片整体包围盒线框,排查瓦片缺失 tileset.debugShowBoundingVolume = true; // 瓦片内部几何包围盒 tileset.debugShowContentBoundingVolume = true; // 标注瓦片几何误差,判断层级精细度 tileset.debugShowGeometricError = true; // 冻结当前瓦片,停止加载新数据 tileset.debugFreezeFrame = true; // 随机分色区分相邻瓦片,看清瓦片边界 tileset.debugColorizeTiles = true;3.2 问题排查速查表
| 现象 | 调试工具 | 判断依据 |
|---|---|---|
| 部分建筑消失 | debugShowBoundingVolume | 无包围盒 = 瓦片请求失败;有盒无图形 = 几何损坏 |
| 远处模型模糊粗糙 | debugShowGeometricError | 几何误差数值过大,调高精细层级 |
| 缩放时画面闪烁 | debugColorizeTiles | 瓦片层级切换边界突兀 |
| 页面加载卡顿 | debugFreezeFrame | 冻结后统计当前渲染瓦片数量 |
四、skipLevelOfDetail 跳级加载优化
4.1 常规逐级加载缺陷
默认瓦片加载严格 0→1→2→3 逐级加载,远距离浏览时长期显示模糊低精度瓦片,缩放拉近时大量请求中间层级瓦片,带宽消耗高、画面刷新慢。 跳级加载:跳过中间层级,直接加载当前视距匹配精度瓦片,大幅减少请求数量。
4.2 使用前置条件
仅瓦片细化策略为REPLACE时生效;ADD 叠加细化瓦片不支持跳级加载,检测代码:
tileset.readyPromise.then(()=>{ const refine = tileset.root.refine; if(refine === Cesium.Cesium3DTileRefine.ADD){ console.warn("ADD细化,跳级加载无效"); } })4.3 完整配置参数
const tileset = await Cesium.Cesium3DTileset.fromUrl("tileset.json",{ skipLevelOfDetail:true, // 开启跳级 skipLevels:1, // 最多跳过1层,保守策略 immediatelyLoadDesiredLevelOfDetail:false // 子瓦片加载时保留父瓦片,防空白闪烁 })4.4 场景参数配置推荐
| 场景 | skipLevelOfDetail | skipLevels | immediatelyLoadDesiredLevelOfDetail |
|---|---|---|---|
| 城市鸟瞰、大范围飞行 | true | 2 | false |
| 室内精细漫游、近距离浏览 | false | - | - |
| 低带宽移动端 | true | 3 | true |
五、maximumMemoryUsage 显存内存管控与瓦片事件
5.1 内存溢出问题
3D Tiles 瓦片几何、纹理存储在 GPU 显存,无上限管控会持续加载,触发浏览器 OOM 崩溃,通过maximumMemoryUsage设置显存硬阈值,超出自动卸载远距离瓦片。
5.2 设备适配内存配置
// 移动端低配:128MB maximumMemoryUsage:128 * 1024 * 1024 // 笔记本集成显卡:256MB maximumMemoryUsage:256 * 1024 * 1024 // 台式独立显卡:512~1024MB maximumMemoryUsage:1024 * 1024 * 10245.3 瓦片全生命周期监听事件
// 单瓦片加载完成/失败 tileset.tileLoad.addEventListener(tile=>{ if(tile.contentFailed) console.warn("瓦片加载失败",tile._header.uri); }) // 瓦片进入视野可见 tileset.tileVisible.addEventListener(tile=>{}) // 当前视野全部瓦片加载完成 tileset.allTilesLoaded.addEventListener(()=>{}) // 初始根瓦片加载完毕 tileset.initialTilesLoaded.addEventListener(()=>{})5.4 简易显存监控工具
setInterval(()=>{ const memMB = (tileset.totalMemoryUsageInBytes / 1024 / 1024).toFixed(1); const limitMB = (tileset.maximumMemoryUsage / 1024 / 1024).toFixed(0); console.log(`显存占用:${memMB}MB,上限${limitMB}MB,使用率${(memMB/limitMB*100).toFixed(1)}%`) },1000)六、综合实战:建筑剖面可视化(剖切 + 着色器 + 跳级)
整合本章全部能力,实现可动态剖切、高度热力渐变、悬停脉冲发光、大场景流畅加载的建筑剖面 Demo:
async function createBuildingView(viewer){ // 1.加载瓦片,开启跳级、内存限制 const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(96188,{ skipLevelOfDetail:true, skipLevels:1, maximumMemoryUsage:512 * 1024 * 1024 }) viewer.scene.primitives.add(tileset); await tileset.readyPromise; // 2.水平剖切平面 const clipPlanes = new Cesium.ClippingPlaneCollection({ planes:[new Cesium.ClippingPlane(new Cesium.Cartesian3(0,0,1),-20)], edgeColor:Cesium.Color.CYAN, edgeWidth:1.5 }) tileset.clippingPlanes = clipPlanes; // 3.热力+脉冲自定义着色器 const customShader = new Cesium.CustomShader({ uniforms:{ u_time:{type:Cesium.UniformType.FLOAT,value:0}, u_hoverH:{type:Cesium.UniformType.FLOAT,value:-1}, u_min:{type:Cesium.UniformType.FLOAT,value:0}, u_max:{type:Cesium.UniformType.FLOAT,value:20} }, fragmentShaderText:` void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material){ float h = fsInput.attributes.positionMC.z; float t = clamp((h-u_min)/(u_max-u_min),0.0,1.0); vec3 heat = mix(vec3(0,0.8,1),vec3(1,0,0),t); material.diffuse = heat; if(abs(h - u_hoverH) < 0.5){ float pulse = 0.5+0.5*sin(u_time*4); vec3 gold = vec3(1,0.85,0.1); material.diffuse = mix(heat,gold,pulse*0.7); material.emissive = gold*pulse*0.5; } } ` }) tileset.customShader = customShader; // 驱动动画时间 viewer.scene.preRender.addEventListener(()=>{ customShader.setUniform("u_time", performance.now()/1000) }) // 鼠标悬停更新楼层高度 const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); handler.setInputAction(move=>{ const pos = viewer.scene.pickPosition(move.endPosition); if(Cesium.defined(pos)){ const cart = Cesium.Cartographic.fromCartesian(pos); customShader.setUniform("u_hoverH", cart.height); }else{ customShader.setUniform("u_hoverH", -1); } },Cesium.ScreenSpaceEventType.MOUSE_MOVE) viewer.camera.flyTo({destination:tileset.boundingSphere.center,orientation:{heading:Cesium.Math.toRadians(45),pitch:Cesium.Math.toRadians(-30),roll:0}}) return {tileset,clipPlanes,customShader} }七、性能调优清单
- 加载优化:大范围场景开启
skipLevelOfDetail,室内漫游关闭;合理调高maximumScreenSpaceError减少瓦片加载数量 - 内存管控:按设备显存设置
maximumMemoryUsage,定期监控显存使用率,超过 90% 降低上限 - 剖切优化:远距离浏览时动态关闭
clippingPlanes.enabled,减少 GPU 平面计算 - 着色器优化:简化 GLSL 分支判断,降低 uniform 每帧更新频率,非动画参数固定值不实时刷新
- 渲染调试:上线前关闭所有 debug 调试开关,避免额外线框绘制消耗性能
本篇总结
ClippingPlaneCollection实现三维模型任意角度剖切,支持单平面水平切楼、多平面组合剖切,滑块交互动态调节切割高度,区分并集 / 交集两种剔除模式,切面可自定义描边。CustomShader自定义 GPU 着色器通过 GLSL 代码实现基础样式无法完成的高级特效,uniform 完成 JS 与着色器数据互通,高度热力、脉冲发光是数字孪生高频业务效果。- 全套
debugShow调试工具可视化瓦片包围盒、几何误差、瓦片边界,快速定位瓦片缺失、层级切换、加载卡顿等渲染问题。 skipLevelOfDetail跳级加载跳过中间瓦片层级,减少网络请求,仅支持 REPLACE 细化瓦片,鸟瞰飞行场景大幅提升加载速度。maximumMemoryUsage限制显存占用,防止大 BIM / 倾斜摄影模型 OOM 崩溃;瓦片生命周期事件实现加载监控、失败捕获、显存统计面板。- 多特性可组合叠加:剖切 + 热力着色器 + 跳级加载实现完整建筑剖面分析系统,是智慧城市、园区 BIM 项目标准解决方案。
课后实操练习
- 剖切实验:分别实现水平切顶部、垂直切侧面、双平面对角剖切,对比 unionClippingRegions 两种模式视觉差异。
- 着色器开发:编写高度渐变热力着色器,增加时间 uniform 实现颜色缓慢流动呼吸动画。
- 瓦片调试:依次开启全部 debug 调试开关,缩放观察瓦片层级切换、包围盒分布规律。
- 内存监控:实现控制台显存监控面板,调低内存上限观察引擎自动卸载远距离瓦片行为。
- 综合进阶:整合剖切滑块、热力着色、悬停脉冲、跳级加载,完成完整建筑剖面浏览 Demo。
下一篇预告
CesiumJS 从入门到精通 19:光影魔术师:光照模型与后期处理特效
讲解全局太阳光、环境光遮蔽 AO、屏幕空间反射 SSR,使用PostProcessStage实现泛光、景深、电影级调色后期特效,打造真实写实三维渲染氛围。