经纬志
首页 / Cesium / 教程

实体与图元

先看结论

Cesium 有两套绘制接口:Entity 面向业务对象,一个实体就是一条业务记录; Primitive 面向 GPU,一批几何合成一次绘制。选错接口,几百个对象就会掉帧。

最后验证 2026-11-03 · CesiumJS 1.121 · 更新 2026-10-27 · 引用本文

两者不是替代关系

对比项EntityPrimitive
抽象层级业务对象,含 id、name、properties 元数据几何体,只有顶点和外观
时间变化图形属性可以是 Property,随 clock 变化需要自己在每帧更新
绘制方式同一类型的实体自动合批手动 GeometryInstance 合批
拾取scene.pick 返回实体本身返回 Primitive,要自己维护映射
数量级数千个以内舒适十万级顶点也只是一次绘制

业务数据(设备、车辆、行政区)用 Entity,能直接挂属性、做 hover 高亮、写 infoBox。 海量点云或规则网格用 Primitive,因为 Entity 每个对象都要走一遍属性系统, 上万个之后开销明显。

一个实体的结构

viewer.entities.add({...}) 接收的是一个配置对象,由三部分组成: 位置、朝向,以及一个或多个图形属性。

viewer.entities.add({
  id: 'station-01',
  name: '气象站 01',
  position: Cesium.Cartesian3.fromDegrees(116.391263, 39.907359, 60),
  point: {
    pixelSize: 10,
    color: Cesium.Color.fromCssColorString('#4d9bff'),
    outlineColor: Cesium.Color.WHITE,
    outlineWidth: 2
  },
  label: {
    text: '气象站 01',
    font: '500 12px sans-serif',
    fillColor: Cesium.Color.WHITE,
    pixelOffset: new Cesium.Cartesian2(0, -20),
    disableDepthTestDistance: Number.POSITIVE_INFINITY
  },
  properties: {                 // 业务字段,拾取后可以读
    temperature: 21.4,
    updatedAt: '2026-10-27T08:00:00Z'
  }
});

id 省略时会自动生成 GUID。需要按业务主键查找时一定要显式给 id, 之后可以用 viewer.entities.getById('station-01') 直接拿到对象。

图形属性一览

属性几何形态常用参数
point屏幕像素圆点pixelSize、color、outlineWidth
billboard图标,始终面向相机image、scale、verticalOrigin
label文字text、font、pixelOffset、style
polyline线positions、width、material、clampToGround
polygon面hierarchy、height、extrudedHeight
wall竖直墙面positions、maximumHeights
modelglTF 模型uri、scale、minimumPixelSize
ellipse / rectangle圆、矩形semiMajorAxis、coordinates

让属性随时间变化

上面每个图形参数都可以写成 Property。ConstantProperty 是静态值, 插值、回调、采样三类属性可以随时间或逐帧变化。

// 采样属性:按给定时间点插值出位置
var position = new Cesium.SampledPositionProperty();
position.addSample(Cesium.JulianDate.fromIso8601('2026-10-27T00:00:00Z'),
  Cesium.Cartesian3.fromDegrees(116.39, 39.90, 100));
position.addSample(Cesium.JulianDate.fromIso8601('2026-10-27T00:05:00Z'),
  Cesium.Cartesian3.fromDegrees(116.45, 39.95, 100));

// 回调属性:每帧重新计算,用来做闪烁或呼吸效果
var pulse = new Cesium.CallbackProperty(function (time, result) {
  var phase = Date.now() % 2000 / 2000;
  return Cesium.Color.fromCssColorString('#4d9bff')
    .withAlpha(0.35 + 0.65 * Math.abs(Math.sin(phase * Math.PI)));
}, false);

CallbackProperty 的第二个参数 isConstant 必须写 false,写成 true 会被当成静态值缓存,动画就不动了。

批量添加要暂停事件

每调用一次 entities.add,集合都会触发一次变更事件,触发重排序和重绘。 一次加几千个对象时,先把事件挂起:

viewer.entities.suspendEvents();
for (var i = 0; i < 5000; i++) {
  viewer.entities.add({ /* ... */ });
}
viewer.entities.resumeEvents();

另一条路是把数据一次性构造成 EntityCollection,再用 viewer.entities = collection 整体替换。两种做法效果相同, 后者在数据完全重载时更干净。

拾取与高亮

scene.pick 的结果里,picked.id 就是 Entity 实例, 可以直接改它的图形属性做 hover 效果:

var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

handler.setInputAction(function (movement) {
  var picked = viewer.scene.pick(movement.endPosition);
  viewer.entities.values.forEach(function (e) {
    e.point.pixelSize = 8;
  });
  if (Cesium.defined(picked) && picked.id instanceof Cesium.Entity) {
    picked.id.point.pixelSize = 14;
  }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

大量实体时不要每帧遍历全表,先用 picked.id 记录上一个高亮对象, 再单独还原它。

四个容易忽略的细节

贴地图元需要地形或椭球面

heightReference: CLAMP_TO_GROUND 只对 point、billboard、label、model 有效, 而且要让 scene.globe.depthTestAgainstTerrain = true 才能被地形正确遮挡。 没开这个开关,贴地的图标会浮在山体前面。

线的宽度上限

大多数平台 WebGL 只保证 1 像素的线宽,width 写 5 可能仍然是 1 像素。 需要粗线时用 polyline.material 配合 Cesium.PolylineVolumeGraphics, 或者直接改用墙面几何模拟。

z-fighting

同一高度的两个面(比如省界和市界都贴地)会闪烁。给其中一个加上 height: 1,或者用 polygon.zIndex 调整绘制顺序。

删除要彻底

entity.show = false 只是不画,对象和它的几何仍在内存里。 数据量滚动更新时应该 viewer.entities.remove(entity), 否则跑一天之后内存会一直涨。

引用本文

经纬志(2026)。《实体与图元》。https://www.jingweizhi.com/cesium/tutorial/entity.html(访问日期 2026-11-03)。