Entity API vs Primitive API 详细对比
Entity API vs Primitive API 详细对比
1. 核心概念
Entity API(高层抽象)
- 面向对象:每个 Entity 代表一个独立的地理对象
- 数据驱动:基于时间动态的属性系统
- 易用性优先:API 设计简洁,上手快
Primitive API(底层图形)
- 面向图形:直接操作 WebGL 渲染层
- 性能优先:批量渲染,减少 draw call
- 灵活性高:可以精细控制渲染细节
2. 代码对比
添加一个多边形
// ========== Entity API ==========
const entity = viewer.entities.add({name: '区域A',polygon: {hierarchy: Cesium.Cartesian3.fromDegreesArray([120, 30,121, 30,121, 31,120, 31]),material: Cesium.Color.RED.withAlpha(0.5),outline: true,outlineColor: Cesium.Color.BLACK,height: 0,extrudedHeight: 100000},properties: {population: 1000000,area: 500}
});// 后续可以动态修改
entity.polygon.material = Cesium.Color.BLUE;
entity.show = false;// ========== Primitive API ==========
const instance = new Cesium.GeometryInstance({geometry: new Cesium.PolygonGeometry({polygonHierarchy: new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray([120, 30,121, 30,121, 31,120, 31])),height: 0,extrudedHeight: 100000}),id: 'area-a',attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED.withAlpha(0.5))}
});const primitive = viewer.scene.primitives.add(new Cesium.Primitive({geometryInstances: instance,appearance: new Cesium.PerInstanceColorAppearance({closed: true,translucent: true})
}));// Primitive 创建后难以修改单个属性
// 需要通过 getGeometryInstanceAttributes 修改
const attributes = primitive.getGeometryInstanceAttributes('area-a');
attributes.color = [0, 0, 255, 128]; // RGBA,需要手动计算
3. 主要区别对比表
| 特性 | Entity API | Primitive API |
|---|---|---|
| 学习曲线 | 简单,10分钟上手 | 复杂,需要理解图形学概念 |
| 性能 | 较差(每个Entity独立渲染) | 优秀(批量渲染) |
| 渲染效率 | 1000个Entity会卡顿 | 可处理10万+实例 |
| 动态修改 | 容易,直接修改属性 | 困难,需要重建或特殊API |
| 时间动态 | 原生支持时间轴动画 | 不支持,需手动实现 |
| 拾取交互 | 自动支持,返回Entity对象 | 需要手动配置ID |
| 数据绑定 | 支持properties自定义属性 | 只能存储ID |
| 聚合 | 支持Clustering | 不支持 |
| 内存占用 | 较高 | 较低 |
4. 渲染原理差异
Entity API 渲染流程
Entity对象 → EntityCollection →
内部转换为Primitive → 每个Entity独立渲染 →
多次WebGL Draw Call
问题:
- 1000个多边形 = 1000次 Draw Call = 卡顿
Primitive API 渲染流程
多个GeometryInstance → 合并为一个Primitive →
一次WebGL Draw Call → 高效渲染
优势:
- 1000个多边形实例 = 1次 Draw Call = 流畅
5. 详细功能对比
5.1 创建多个对象
// ========== Entity API ==========
// 缺点:每个对象独立渲染
for (let i = 0; i < 1000; i++) {viewer.entities.add({position: Cesium.Cartesian3.fromDegrees(120 + i * 0.01, 30),point: {pixelSize: 10,color: Cesium.Color.RED}});
}
// 结果:1000次Draw Call,严重卡顿// ========== Primitive API ==========
// 优点:批量渲染
const pointCollection = viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection()
);for (let i = 0; i < 1000; i++) {pointCollection.add({position: Cesium.Cartesian3.fromDegrees(120 + i * 0.01, 30),pixelSize: 10,color: Cesium.Color.RED});
}
// 结果:1次Draw Call,流畅
5.2 动态修改
// ========== Entity API ==========
const entity = viewer.entities.add({position: Cesium.Cartesian3.fromDegrees(120, 30),point: {pixelSize: 10,color: Cesium.Color.RED}
});// 轻松修改
entity.point.color = Cesium.Color.BLUE;
entity.position = Cesium.Cartesian3.fromDegrees(121, 31);
entity.show = false;// ========== Primitive API ==========
const pointCollection = viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection()
);const point = pointCollection.add({position: Cesium.Cartesian3.fromDegrees(120, 30),pixelSize: 10,color: Cesium.Color.RED,id: 'point-1'
});// 可以直接修改(Collection类型支持)
point.color = Cesium.Color.BLUE;
point.position = Cesium.Cartesian3.fromDegrees(121, 31);
point.show = false;// 但对于Primitive,修改颜色需要特殊API
const primitive = viewer.scene.primitives.add(new Cesium.Primitive({geometryInstances: instance,appearance: new Cesium.PerInstanceColorAppearance()
}));// 修改颜色
const attributes = primitive.getGeometryInstanceAttributes('id');
attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.BLUE
);
5.3 时间动态属性
// ========== Entity API ==========
// 原生支持时间线动画
const entity = viewer.entities.add({position: new Cesium.SampledPositionProperty(),point: {pixelSize: 10,color: new Cesium.SampledProperty(Cesium.Color)}
});// 添加时间采样点
const start = Cesium.JulianDate.now();
for (let i = 0; i < 100; i++) {const time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());// 位置随时间变化entity.position.addSample(time,Cesium.Cartesian3.fromDegrees(120 + i * 0.01, 30));// 颜色随时间变化entity.point.color.addSample(time,Cesium.Color.fromHsl(i / 100, 1, 0.5));
}viewer.clock.shouldAnimate = true; // 自动播放// ========== Primitive API ==========
// 不支持时间动态,需手动实现
const pointCollection = viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection()
);const point = pointCollection.add({position: Cesium.Cartesian3.fromDegrees(120, 30),pixelSize: 10,color: Cesium.Color.RED
});// 手动监听时钟更新
viewer.clock.onTick.addEventListener((clock) => {const currentTime = clock.currentTime;// 手动计算当前时间的位置和颜色point.position = calculatePositionAtTime(currentTime);point.color = calculateColorAtTime(currentTime);
});
5.4 交互拾取
// ========== Entity API ==========
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);handler.setInputAction((click) => {const pickedObject = viewer.scene.pick(click.position);if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {const entity = pickedObject.id; // 直接获取Entity对象console.log('点击了:', entity.name);console.log('属性:', entity.properties);// 高亮显示entity.polygon.material = Cesium.Color.YELLOW;}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);// ========== Primitive API ==========
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);handler.setInputAction((click) => {const pickedObject = viewer.scene.pick(click.position);if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.primitive)) {const primitive = pickedObject.primitive;const id = pickedObject.id; // GeometryInstance的ID(字符串)console.log('点击了ID:', id);// 需要自己维护ID到数据的映射const data = myDataMap.get(id);// 修改颜色(复杂)const attributes = primitive.getGeometryInstanceAttributes(id);if (attributes) {attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.YELLOW);}}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
6. 性能测试对比
测试场景:渲染10000个多边形
// ========== Entity API ==========
console.time('Entity加载');
for (let i = 0; i < 10000; i++) {viewer.entities.add({polygon: {hierarchy: Cesium.Cartesian3.fromDegreesArray([120 + i * 0.001, 30,120 + i * 0.001 + 0.001, 30,120 + i * 0.001 + 0.001, 30.001,120 + i * 0.001, 30.001]),material: Cesium.Color.RED.withAlpha(0.5)}});
}
console.timeEnd('Entity加载');
// 结果:约 15000ms,FPS < 10,严重卡顿// ========== Primitive API ==========
console.time('Primitive加载');
const instances = [];
for (let i = 0; i < 10000; i++) {instances.push(new Cesium.GeometryInstance({geometry: new Cesium.PolygonGeometry({polygonHierarchy: new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray([120 + i * 0.001, 30,120 + i * 0.001 + 0.001, 30,120 + i * 0.001 + 0.001, 30.001,120 + i * 0.001, 30.001]))}),id: `polygon-${i}`,attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED.withAlpha(0.5))}}));
}viewer.scene.primitives.add(new Cesium.Primitive({geometryInstances: instances,appearance: new Cesium.PerInstanceColorAppearance({translucent: true})
}));
console.timeEnd('Primitive加载');
// 结果:约 2000ms,FPS 50-60,流畅
性能提升:7.5倍
7. Primitive API 的特殊类型
Primitive API 有多种优化的集合类型:
// 点集合
const pointCollection = new Cesium.PointPrimitiveCollection();
viewer.scene.primitives.add(pointCollection);// 广告牌集合
const billboardCollection = new Cesium.BillboardCollection();
viewer.scene.primitives.add(billboardCollection);// 标签集合
const labelCollection = new Cesium.LabelCollection();
viewer.scene.primitives.add(labelCollection);// 折线集合
const polylineCollection = new Cesium.PolylineCollection();
viewer.scene.primitives.add(polylineCollection);// 通用Primitive(多边形等)
const primitive = new Cesium.Primitive({geometryInstances: [...],appearance: new Cesium.PerInstanceColorAppearance()
});
viewer.scene.primitives.add(primitive);
8. 选择建议
使用 Entity API 的场景:
✅ 数据量小(< 1000个对象)
✅ 需要频繁修改属性
✅ 需要时间动态效果
✅ 快速原型开发
✅ 需要数据绑定和属性管理
✅ 使用DataSource加载GeoJSON/KML
使用 Primitive API 的场景:
✅ 大数据量(> 1000个对象)
✅ 性能要求高
✅ 静态展示为主
✅ 需要自定义Shader
✅ 批量渲染相似对象
✅ 生产环境的优化需求
9. 混合使用策略
class HybridRenderer {constructor(viewer) {this.viewer = viewer;// 少量重要对象用Entity(可交互)this.entities = viewer.entities;// 大量背景数据用Primitive(高性能)this.primitives = viewer.scene.primitives;}// 添加可交互的重点对象addInteractiveObject(data) {return this.entities.add({name: data.name,polygon: {hierarchy: data.positions,material: Cesium.Color.RED},properties: data.properties});}// 批量添加背景对象addBackgroundObjects(dataArray) {const instances = dataArray.map(data => new Cesium.GeometryInstance({geometry: new Cesium.PolygonGeometry({polygonHierarchy: new Cesium.PolygonHierarchy(data.positions)}),id: data.id,attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.GRAY.withAlpha(0.3))}}));this.primitives.add(new Cesium.Primitive({geometryInstances: instances,appearance: new
