Cesium 优化:高效加载 10 万点位的实现方案
在 WebGIS 应用开发中,大量点位标记的高效渲染一直是一个挑战。特别是在 Cesium 这样的三维地图引擎中,当需要同时展示上万甚至十万级别的点位标记时,性能问题尤为突出。本文将分享一种在 Cesium 中高效加载和渲染 10 万级点位的实现方案,重点关注 BillboardCollection 的优化配置。
问题背景
在实际业务中,我们经常需要在地图上展示大量的点位标记,例如:
- 全国范围内的监测站点数据
- 城市级别的物联网设备分布
- 人口热力图点位
- 轨迹点位数据
当这些点位数量达到万级甚至十万级时,常规的渲染方式会导致严重的性能问题:
- 页面加载缓慢,甚至浏览器崩溃
- 交互操作卡顿,FPS 大幅下降
- 内存占用过高
优化方案
通过实验发现,Cesium 的 BillboardCollection 在创建时,如果传入scene参数,可以显著提升大量点位的渲染性能。这是因为传入 scene 参数后,BillboardCollection 会使用 WebGL 的实例化渲染(Instanced Arrays)技术,大幅减少渲染调用次数。
下面是一个加载 10 万点位的优化实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| const Cesium = SmartEarth.Cesium; window.earthCtrl = new SmartEarth.EarthCtrl("sdkContainer", {}); window.viewer = window.earthCtrl.viewer; earthCtrl.showFPS = true; const ellipsoid = viewer.scene.globe.ellipsoid; let billboardCollection; let instancingEnabled = true;
let billboardCount = 100489; let scale = 1.0;
const centerLongitude = -1.385205433269729; const centerLatitude = 0.6777926580888163; const rectangleHalfSize = 0.5; const e = new Cesium.Rectangle( centerLongitude - rectangleHalfSize, centerLatitude - rectangleHalfSize, centerLongitude + rectangleHalfSize, centerLatitude + rectangleHalfSize ); resetBillboardCollection(); function resetBillboardCollection() { if (Cesium.defined(billboardCollection)) { viewer.scene.primitives.remove(billboardCollection); } billboardCollection = viewer.scene.primitives.add( new Cesium.BillboardCollection({ scene: viewer.scene, }) ); const gridSize = Math.sqrt(billboardCount); for (let y = 0; y < gridSize; ++y) { for (let x = 0; x < gridSize; ++x) { const longitude = Cesium.Math.lerp(e.west, e.east, x / (gridSize - 1)); const latitude = Cesium.Math.lerp(e.south, e.north, y / (gridSize - 1)); const position = new Cesium.Cartographic(longitude, latitude, 10000.0); billboardCollection.add({ position: ellipsoid.cartographicToCartesian(position), image: "./demo/image/water.svg", scale: scale, }); } } }
|
代码解析
让我们详细分析一下代码的关键部分:
1. 核心优化点
1 2 3 4 5
| billboardCollection = viewer.scene.primitives.add( new Cesium.BillboardCollection({ scene: viewer.scene, }) );
|
这里的关键优化是在创建 BillboardCollection 时传入scene参数。这看似简单的一步却有巨大的性能提升,主要原因是:
- 启用了 WebGL 的 Instanced Arrays 扩展功能
- 减少了 CPU 到 GPU 的数据传输次数
- 降低了 WebGL 的绘制调用(draw calls)次数
2. 点位生成策略
1 2 3 4 5 6 7 8
| const gridSize = Math.sqrt(billboardCount); for (let y = 0; y < gridSize; ++y) { for (let x = 0; x < gridSize; ++x) { const longitude = Cesium.Math.lerp(e.west, e.east, x / (gridSize - 1)); const latitude = Cesium.Math.lerp(e.south, e.north, y / (gridSize - 1)); } }
|
这段代码采用网格分布策略生成点位:
- 计算网格大小为点位总数的平方根(约 317×317 网格)
- 使用双重循环填充整个矩形区域
- 通过线性插值(
Cesium.Math.lerp)均匀分布点位
3. 点位创建
1 2 3 4 5 6
| const position = new Cesium.Cartographic(longitude, latitude, 10000.0); billboardCollection.add({ position: ellipsoid.cartographicToCartesian(position), image: "./demo/image/water.svg", scale: scale, });
|
每个点位的创建过程:
- 创建经纬度高度的笛卡尔坐标
- 转换为地心坐标系(ECEF)
- 使用统一的 SVG 图标
- 应用统一的缩放比例
性能对比
实验表明,添加scene参数前后的性能差异非常显著:
| 配置 |
点位数量 |
平均 FPS |
加载时间 |
内存占用 |
| 不带 scene 参数 |
100,489 |
<10 |
>10 秒 |
高 |
| 带 scene 参数 |
100,489 |
>30 |
<3 秒 |
中等 |

通过截图可以看到,使用优化方案后,即使是在渲染 10 万级别的点位时,FPS 仍然能够维持在 30 以上,确保了良好的用户体验。
总结
通过在 BillboardCollection 中正确配置scene参数,结合其他优化技巧,我们成功实现了在 Cesium 中高效渲染 10 万级点位的功能。这种方法大幅提升了渲染性能,降低了内存占用,使得大规模点位可视化成为可能。