写在前面
前序章节掌握 3D Tiles 基础加载、Cesium3DTileStyle属性着色,仅能完成基础可视化;工业 BIM、倾斜摄影数字孪生项目常需要剖切查看内部结构、自定义动态光影特效、大场景流畅加载、显存防崩溃管控。 本章讲解五大生产级核心能力:ClippingPlaneCollection模型剖切、CustomShaderGPU 自定义着色器、debug 瓦片调试工具、skipLevelOfDetail跳级加速加载、maximumMemoryUsage内存管控,配套多特性组合综合案例,覆盖建筑剖面、动态热力、大场景性能优化业务需求。
本章学习目标
一、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 两种裁剪模式对比
| 逻辑 | 任一平面剔除则隐藏 | 仅多平面重叠区域剔除 |
| 效果 | 平面越多,剔除范围越大 | 平面越多,剔除范围越小 |
| 适用 | 顶部、侧面多面同时剖切 | 只切除建筑角落一小块 |
二、CustomShader 自定义 GPU 着色器
2.1 适用场景
基础Cesium3DTileStyle仅支持简单静态着色,无法实现:高度渐变热力、脉冲呼吸发光、实时数据映射、菲涅尔边缘光;CustomShader 直接编写 GLSL 片元 / 顶点着色代码,在 GPU 并行运算,实现高级视觉特效。
2.2 基础结构说明
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 场景参数配置推荐
| 城市鸟瞰、大范围飞行 | 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 * 1024
5.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}
}
七、性能调优清单
本篇总结
课后实操练习
下一篇预告
CesiumJS 从入门到精通 19:光影魔术师:光照模型与后期处理特效
讲解全局太阳光、环境光遮蔽 AO、屏幕空间反射 SSR,使用PostProcessStage实现泛光、景深、电影级调色后期特效,打造真实写实三维渲染氛围。



