手把手教你用Cesium3DTileset给楼宇白膜‘一键换肤’(含样式代码)

张开发
2026/4/12 16:02:32 15 分钟阅读

分享文章

手把手教你用Cesium3DTileset给楼宇白膜‘一键换肤’(含样式代码)
Cesium3DTileset楼宇白膜高级样式定制实战指南当你在Cesium中加载了3D Tiles格式的楼宇白膜数据后那些单调的白色方块是否让你感到视觉疲劳本文将带你突破基础渲染的局限通过Cesium3DTileStyle实现从白模到精模的华丽转变。不同于简单的单色设置我们将深入探索如何基于楼宇属性动态着色、模拟真实光照效果并实现交互式高亮——这些技巧能让你的三维城市可视化项目瞬间脱颖而出。1. 环境准备与基础样式设置在开始高级样式定制前确保你已经正确加载了3D Tiles数据。以下是初始化Cesium场景和加载白膜的基本代码框架const viewer new Cesium.Viewer(cesiumContainer, { terrainProvider: Cesium.createWorldTerrain() }); const tileset await Cesium.Cesium3DTileset.fromUrl(path/to/tileset.json); viewer.scene.primitives.add(tileset); viewer.zoomTo(tileset);要给白膜设置统一的颜色最基础的方法是使用Cesium3DTileStyletileset.style new Cesium.Cesium3DTileStyle({ color: color(rgb(200, 200, 200), 0.8) // 浅灰色80%不透明度 });注意直接设置color属性会影响整个tileset中所有建筑物的外观。2. 基于属性的条件着色技术真正的威力来自于根据楼宇属性动态设置样式。假设你的3D Tiles数据包含以下属性height楼宇高度type建筑类型住宅、商业、工业等yearBuilt建造年份2.1 按建筑类型分类着色tileset.style new Cesium.Cesium3DTileStyle({ color: { conditions: [ [${feature[type]} residential, color(rgb(255, 200, 150))], [${feature[type]} commercial, color(rgb(150, 200, 255))], [${feature[type]} industrial, color(rgb(200, 255, 150))], [true, color(rgb(200, 200, 200))] // 默认颜色 ] } });2.2 基于高度的渐变色彩通过Cesium的表达式语言我们可以实现根据楼宇高度变化的颜色渐变tileset.style new Cesium.Cesium3DTileStyle({ color: { expression: rgba(100, 150 clamp(${feature[height]} * 2, 0, 105), 255, 0.8) } });提示clamp函数确保颜色值在合理范围内避免溢出。2.3 多属性组合条件更复杂的场景可能需要结合多个属性进行样式判断tileset.style new Cesium.Cesium3DTileStyle({ color: { conditions: [ [${feature[type]} historic ${feature[height]} 50, color(red)], [${feature[type]} historic, color(orange)], [${feature[yearBuilt]} 2000, color(blue)], [true, color(white)] ] } });3. 高级视觉效果增强3.1 模拟光照与阴影即使没有真实的3D模型细节也可以通过样式模拟基本的光照效果tileset.style new Cesium.Cesium3DTileStyle({ color: { expression: vec4 baseColor vec4(0.8, 0.8, 0.8, 1.0); float light dot(normalize(vec3(1.0, 1.0, 0.5)), ${normal}); light light * 0.5 0.5; // 调整光照强度范围 baseColor.rgb * light; baseColor } });3.2 动态透明度变化创建随时间或视角变化的透明度效果let startTime Cesium.JulianDate.now(); viewer.clock.onTick.addEventListener(function() { const currentTime Cesium.JulianDate.secondsDifference( viewer.clock.currentTime, startTime ); tileset.style new Cesium.Cesium3DTileStyle({ color: { expression: color(white, 0.5 sin(${currentTime} * 0.5) * 0.2) } }); });4. 交互式高亮与选择4.1 鼠标悬停高亮const highlightStyle new Cesium.Cesium3DTileStyle({ color: color(yellow) }); const defaultStyle tileset.style; handler.setInputAction(function(movement) { const pickedFeature viewer.scene.pick(movement.endPosition); if (Cesium.defined(pickedFeature) pickedFeature instanceof Cesium.Cesium3DTileFeature) { tileset.style highlightStyle; } else { tileset.style defaultStyle; } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);4.2 点击选择与信息展示handler.setInputAction(function(click) { const pickedFeature viewer.scene.pick(click.position); if (Cesium.defined(pickedFeature) pickedFeature instanceof Cesium.Cesium3DTileFeature) { // 临时改变选中建筑颜色 pickedFeature.color Cesium.Color.YELLOW; // 显示属性信息 const properties pickedFeature.getPropertyNames(); let html table; properties.forEach(prop { html trtd${prop}/tdtd${pickedFeature.getProperty(prop)}/td/tr; }); html /table; viewer.selectedEntity new Cesium.Entity({ description: html }); } }, Cesium.ScreenSpaceEventType.LEFT_CLICK);5. 性能优化技巧当应用复杂样式时性能考虑尤为重要样式预处理尽可能在样式定义中完成计算而非通过事件监听实时更新条件简化复杂的条件语句拆分为多个简单条件缓存机制对不常变化的样式结果进行缓存// 性能较差的写法 tileset.style new Cesium.Cesium3DTileStyle({ color: { expression: someComplexFunction(${feature[property]}) } }); // 优化后的写法 tileset.style new Cesium.Cesium3DTileStyle({ color: { conditions: [ [${feature[property]} 100, color(red)], [${feature[property]} 50, color(orange)], [true, color(white)] ] } });6. 实战案例城市热力图效果结合上述技术我们可以创建一个模拟城市热力图的视觉效果tileset.style new Cesium.Cesium3DTileStyle({ color: { expression: float height ${feature[height]}; vec3 lowColor vec3(0.0, 0.0, 1.0); // 蓝色 vec3 highColor vec3(1.0, 0.0, 0.0); // 红色 float ratio clamp((height - 20.0) / 100.0, 0.0, 1.0); vec3 finalColor mix(lowColor, highColor, ratio); vec4(finalColor, 0.9) }, show: ${feature[height]} 5 // 只显示高度大于5米的建筑 });在实际项目中遇到的一个有趣挑战是如何处理没有高度属性的数据集。这时可以使用建筑底面面积作为替代指标tileset.style new Cesium.Cesium3DTileStyle({ color: { expression: float area length(${boundingVolume}.rectangle.width) * length(${boundingVolume}.rectangle.height); vec3(1.0, 1.0 - clamp(area / 1000.0, 0.0, 1.0), 0.0) } });7. 样式调试与问题排查当样式不按预期工作时这些调试技巧可能会帮到你检查属性名称确认使用的属性确实存在于数据中简化表达式从最简单的条件开始测试逐步增加复杂度控制台输出使用console.log查看feature属性handler.setInputAction(function(click) { const pickedFeature viewer.scene.pick(click.position); if (Cesium.defined(pickedFeature)) { console.log(Feature properties:, pickedFeature.getPropertyNames().map(name ({ name, value: pickedFeature.getProperty(name) })) ); } }, Cesium.ScreenSpaceEventType.LEFT_CLICK);一个常见错误是属性名大小写不匹配。Cesium3DTileStyle中的属性引用是区分大小写的// 错误如果属性实际是HEIGHT而非height color: color(red, ${feature[height]} / 100.0) // 正确使用实际的属性名 color: color(red, ${feature[HEIGHT]} / 100.0)8. 进阶技巧动态样式切换为你的应用添加样式切换功能让用户可以交互式选择不同的可视化方案div idstyleControls button onclicksetStyle(height)按高度着色/button button onclicksetStyle(type)按类型着色/button button onclicksetStyle(heatmap)热力图模式/button /div script const stylePresets { height: { color: { expression: rgba(100, 150 ${feature[height]}, 255, 0.8) } }, type: { color: { conditions: [ [${feature[type]} residential, color(rgb(255, 200, 150))], [${feature[type]} commercial, color(rgb(150, 200, 255))], [true, color(rgb(200, 200, 200))] ] } }, heatmap: { color: { expression: float ratio clamp((${feature[height]} - 20.0) / 100.0, 0.0, 1.0); vec3 finalColor mix(vec3(0.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), ratio); vec4(finalColor, 0.9) } } }; function setStyle(styleName) { tileset.style new Cesium.Cesium3DTileStyle(stylePresets[styleName]); } /script在处理大规模城市数据集时发现性能瓶颈往往出现在样式更新阶段。一个实用的优化策略是将样式分为几个层级根据视图范围动态调整viewer.scene.postRender.addEventListener(function() { const cameraHeight viewer.camera.positionCartographic.height; if (cameraHeight 5000) { // 远距离视图 tileset.style new Cesium.Cesium3DTileStyle({ color: color(rgb(200, 200, 200), 0.7) }); } else if (cameraHeight 1000) { // 中距离视图 tileset.style new Cesium.Cesium3DTileStyle({ color: { conditions: [ [${feature[type]} residential, color(rgb(255, 200, 150))], [${feature[type]} commercial, color(rgb(150, 200, 255))], [true, color(rgb(200, 200, 200))] ] } }); } else { // 近距离视图 tileset.style new Cesium.Cesium3DTileStyle({ color: { expression: float ratio clamp((${feature[height]} - 20.0) / 100.0, 0.0, 1.0); vec3 finalColor mix(vec3(0.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), ratio); vec4(finalColor, 0.9) } }); } });

更多文章