cesium加载10万点

Cesium 优化:高效加载 10 万点位的实现方案

在 WebGIS 应用开发中,大量点位标记的高效渲染一直是一个挑战。特别是在 Cesium 这样的三维地图引擎中,当需要同时展示上万甚至十万级别的点位标记时,性能问题尤为突出。本文将分享一种在 Cesium 中高效加载和渲染 10 万级点位的实现方案,重点关注 BillboardCollection 的优化配置。

问题背景

在实际业务中,我们经常需要在地图上展示大量的点位标记,例如:

  • 全国范围内的监测站点数据
  • 城市级别的物联网设备分布
  • 人口热力图点位
  • 轨迹点位数据

当这些点位数量达到万级甚至十万级时,常规的渲染方式会导致严重的性能问题:

  1. 页面加载缓慢,甚至浏览器崩溃
  2. 交互操作卡顿,FPS 大幅下降
  3. 内存占用过高

优化方案

通过实验发现,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;
// const instancedArraysExtension = context._instancedArrays;
let billboardCount = 100489;
let scale = 1.0;
// Define the area where billboards will be placed
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,
});

每个点位的创建过程:

  1. 创建经纬度高度的笛卡尔坐标
  2. 转换为地心坐标系(ECEF)
  3. 使用统一的 SVG 图标
  4. 应用统一的缩放比例

性能对比

实验表明,添加scene参数前后的性能差异非常显著:

配置 点位数量 平均 FPS 加载时间 内存占用
不带 scene 参数 100,489 <10 >10 秒 高
带 scene 参数 100,489 >30 <3 秒 中等

性能对比

通过截图可以看到,使用优化方案后,即使是在渲染 10 万级别的点位时,FPS 仍然能够维持在 30 以上,确保了良好的用户体验。

总结

通过在 BillboardCollection 中正确配置scene参数,结合其他优化技巧,我们成功实现了在 Cesium 中高效渲染 10 万级点位的功能。这种方法大幅提升了渲染性能,降低了内存占用,使得大规模点位可视化成为可能。

作者

wangyawei

发布于

2025-03-20

更新于

2026-09-11

许可协议