第18章:CesiumJS 从入门到精通 18:建筑师的工具:3D Tiles 变换裁剪与自定义着色器
2026/9/16 13:51:12 网站建设 项目流程

写在前面

前序章节掌握 3D Tiles 基础加载、Cesium3DTileStyle属性着色,仅能完成基础可视化;工业 BIM、倾斜摄影数字孪生项目常需要剖切查看内部结构、自定义动态光影特效、大场景流畅加载、显存防崩溃管控。 本章讲解五大生产级核心能力:ClippingPlaneCollection模型剖切、CustomShaderGPU 自定义着色器、debug 瓦片调试工具、skipLevelOfDetail跳级加速加载、maximumMemoryUsage内存管控,配套多特性组合综合案例,覆盖建筑剖面、动态热力、大场景性能优化业务需求。

本章学习目标

  1. 掌握ClippingPlane单 / 多平面剖切,区分并集 / 交集裁剪模式,实现滑块动态剖切交互
  2. 理解CustomShader顶点 / 片元着色器、uniform 传参、GLSL 基础语法,实现高度热力、脉冲发光特效
  3. 熟练使用全套debugShow调试工具,快速定位瓦片缺失、层级切换、加载异常问题
  4. 掌握跳级加载策略,区分 REPLACE/ADD 瓦片细化规则,适配鸟瞰 / 室内漫游不同场景
  5. 配置显存内存上限,监听瓦片全生命周期事件,实现内存监控,解决大模型 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 基础结构说明

  1. uniforms:JS 向 GLSL 传递全局变量(颜色、时间、阈值)
  2. vertexShaderText:顶点着色,修改模型几何顶点位置
  3. fragmentShaderText:片元着色,修改每个像素最终颜色(最常用)
  4. 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 场景参数配置推荐

场景skipLevelOfDetailskipLevelsimmediatelyLoadDesiredLevelOfDetail
城市鸟瞰、大范围飞行true2false
室内精细漫游、近距离浏览false--
低带宽移动端true3true

五、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} }

七、性能调优清单

  1. 加载优化:大范围场景开启skipLevelOfDetail,室内漫游关闭;合理调高maximumScreenSpaceError减少瓦片加载数量
  2. 内存管控:按设备显存设置maximumMemoryUsage,定期监控显存使用率,超过 90% 降低上限
  3. 剖切优化:远距离浏览时动态关闭clippingPlanes.enabled,减少 GPU 平面计算
  4. 着色器优化:简化 GLSL 分支判断,降低 uniform 每帧更新频率,非动画参数固定值不实时刷新
  5. 渲染调试:上线前关闭所有 debug 调试开关,避免额外线框绘制消耗性能

本篇总结

  1. ClippingPlaneCollection实现三维模型任意角度剖切,支持单平面水平切楼、多平面组合剖切,滑块交互动态调节切割高度,区分并集 / 交集两种剔除模式,切面可自定义描边。
  2. CustomShader自定义 GPU 着色器通过 GLSL 代码实现基础样式无法完成的高级特效,uniform 完成 JS 与着色器数据互通,高度热力、脉冲发光是数字孪生高频业务效果。
  3. 全套debugShow调试工具可视化瓦片包围盒、几何误差、瓦片边界,快速定位瓦片缺失、层级切换、加载卡顿等渲染问题。
  4. skipLevelOfDetail跳级加载跳过中间瓦片层级,减少网络请求,仅支持 REPLACE 细化瓦片,鸟瞰飞行场景大幅提升加载速度。
  5. maximumMemoryUsage限制显存占用,防止大 BIM / 倾斜摄影模型 OOM 崩溃;瓦片生命周期事件实现加载监控、失败捕获、显存统计面板。
  6. 多特性可组合叠加:剖切 + 热力着色器 + 跳级加载实现完整建筑剖面分析系统,是智慧城市、园区 BIM 项目标准解决方案。

课后实操练习

  1. 剖切实验:分别实现水平切顶部、垂直切侧面、双平面对角剖切,对比 unionClippingRegions 两种模式视觉差异。
  2. 着色器开发:编写高度渐变热力着色器,增加时间 uniform 实现颜色缓慢流动呼吸动画。
  3. 瓦片调试:依次开启全部 debug 调试开关,缩放观察瓦片层级切换、包围盒分布规律。
  4. 内存监控:实现控制台显存监控面板,调低内存上限观察引擎自动卸载远距离瓦片行为。
  5. 综合进阶:整合剖切滑块、热力着色、悬停脉冲、跳级加载,完成完整建筑剖面浏览 Demo。

下一篇预告

CesiumJS 从入门到精通 19:光影魔术师:光照模型与后期处理特效

讲解全局太阳光、环境光遮蔽 AO、屏幕空间反射 SSR,使用PostProcessStage实现泛光、景深、电影级调色后期特效,打造真实写实三维渲染氛围。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询