Cesium中Entity与Primitive结合使用指南
📊 Entity与Primitive关系流程图
🔍 Entity与Primitive基础比较
Entity特点
// Entity示例 - 高级API,简单易用
const entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(116.39, 39.9),
point: {
pixelSize: 10,
color: Cesium.Color.RED
},
label: {
text: '北京'
}
});
Primitive特点
// Primitive示例 - 底层API,性能更好
const positions = Cesium.Cartesian3.fromDegreesArray([
116.39, 39.9,
116.40, 39.9,
116.40, 39.91,
116.39, 39.91
]);
const polygonInstance = new Cesium.GeometryInstance({
geometry: new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(positions),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.RED.withAlpha(0.7)
)
}
});
const primitive = new Cesium.Primitive({
geometryInstances: polygonInstance,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: true
})
});
viewer.scene.primitives.add(primitive);
🧠 两者优缺点对比
| 特性 | Entity | Primitive |
|---|---|---|
| API级别 | ⭐高级,简单易用 | ⭐底层,复杂但更灵活 |
| 性能 | 中等,转换为Primitive渲染 | ⭐高,直接渲染 |
| 拾取 | ⭐自带实体拾取,易于互动 | 手动实现拾取逻辑 |
| 数据量 | 适合小到中等数据量 | ⭐适合大数据量(10000+) |
| 动画 | ⭐简单实现动态效果 | 需手动更新几何体 |
| 内存 | 占用较多 | ⭐更高效 |
| 批处理 | 逐个添加 | ⭐支持几何实例合并 |
🔄 Entity转Primitive技术
1. 通过API直接转换
// ⭐使用Entity API创建,但转换为Primitive渲染
function createEntityAsPrimitive() {
// 1. 创建一个不添加到viewer的Entity
const entity = new Cesium.Entity({
position: Cesium.Cartesian3.fromDegrees(116.39, 39.9),
ellipse: {
semiMajorAxis: 500,
semiMinorAxis: 300,
material: Cesium.Color.RED.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2
}
});
// 2. 创建visualizer来转换Entity
const entityCluster = new Cesium.EntityCluster();
entityCluster._scene = viewer.scene;
const visualizerCollection = new Cesium.VisualizerCollection([
new Cesium.GeometryVisualizer(new Cesium.GeometryInstance(), viewer.scene, entityCluster)
]);
// 3. 处理Entity
visualizerCollection.update(Cesium.JulianDate.now(), [entity]);
// 现在Entity被转换成了Primitive并添加到场景中
}
2. 使用工具函数手动映射
// ⭐Entity到Primitive的映射转换
function createPolygonEntityAsPrimitive(positions, color) {
// 使用Entity API创建Primitive - 手动映射方式
// 1. 从Entity参数构建Primitive参数
const polygonHierarchy = new Cesium.PolygonHierarchy(positions);
// 2. 创建几何实例
const instance = new Cesium.GeometryInstance({
geometry: new Cesium.PolygonGeometry({
polygonHierarchy: polygonHierarchy,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
},
id: 'polygon-' + Math.random().toString(36).substring(2, 9) // 生成随机ID
});
// 3. 创建Primitive
const primitive = new Cesium.Primitive({
geometryInstances: instance,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: true
})
});
// 4. 添加到场景
viewer.scene.primitives.add(primitive);
return primitive;
}
// 使用示例
const positions = Cesium.Cartesian3.fromDegreesArray([
116.39, 39.9,
116.40, 39.9,
116.40, 39.91,
116.39, 39.91
]);
const polygonPrimitive = createPolygonEntityAsPrimitive(
positions,
Cesium.Color.BLUE.withAlpha(0.5)
);
📦 创建批量Primitive高性能图形
// ⭐批量创建点Primitive
function createPointsPrimitive(positions, colors) {
// GLSL着色器 - 点大小由属性控制
const vs = `
attribute vec3 position;
attribute vec4 color;
attribute float pointSize;
varying vec4 v_color;
void main() {
gl_Position = czm_modelViewProjection * vec4(position, 1.0);
gl_PointSize = pointSize;
v_color = color;
}
`;
const fs = `
varying vec4 v_color;
void main() {
float dist = distance(gl_PointCoord, vec2(0.5));
if (dist > 0.5) {
discard;
}
gl_FragColor = v_color;
}
`;
// 创建几何体
const geometry = new Cesium.Geometry({
attributes: {
position: new Cesium.GeometryAttribute({
componentDatatype: Cesium.ComponentDatatype.DOUBLE,
componentsPerAttribute: 3,
values: new Float64Array(positions)
}),
color: new Cesium.GeometryAttribute({
componentDatatype: Cesium.ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute: 4,
values: new Uint8Array(colors),
normalize: true
}),
pointSize: new Cesium.GeometryAttribute({
componentDatatype: Cesium.ComponentDatatype.FLOAT,
componentsPerAttribute: 1,
values: new Float32Array(positions.length / 3).fill(10.0) // 所有点大小为10
})
},
primitiveType: Cesium.PrimitiveType.POINTS
});
// 创建自定义命令
const drawCommand = new Cesium.DrawCommand({
vertexArray: Cesium.VertexArray.fromGeometry({
context: viewer.scene.context,
geometry: geometry,
attributeLocations: {
position: 0,
color: 1,
pointSize: 2
},
bufferUsage: Cesium.BufferUsage.STATIC_DRAW
}),
primitiveType: Cesium.PrimitiveType.POINTS,
shaderProgram: Cesium.ShaderProgram.fromCache({
context: viewer.scene.context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: {
position: 0,
color: 1,
pointSize: 2
}
}),
pass: Cesium.Pass.OPAQUE
});
// 创建自定义primitive
const customPrimitive = new Cesium.Primitive({
geometryInstances: [],
releaseGeometryInstances: false,
update: function(frameState) {
frameState.commandList.push(drawCommand);
}
});
viewer.scene.primitives.add(customPrimitive);
return customPrimitive;
}
// 使用示例 - 创建10000个随机点
function createManyPoints(count) {
const positions = [];
const colors = [];
for (let i = 0; i < count; i++) {
// 生成随机位置
const lon = 116.39 + (Math.random() - 0.5) * 0.5;
const lat = 39.9 + (Math.random() - 0.5) * 0.5;
const height = Math.random() * 1000;
const cartesian = Cesium.Cartesian3.fromDegrees(lon, lat, height);
positions.push(cartesian.x, cartesian.y, cartesian.z);
// 生成随机颜色
colors.push(
Math.floor(Math.random() * 255), // R
Math.floor(Math.random() * 255), // G
Math.floor(Math.random() * 255), // B
255 // A
);
}
return createPointsPrimitive(positions, colors);
}
// 创建10000个点
const pointsPrimitive = createManyPoints(10000);
🔄 混合使用策略
1. 场景中同时使用两者
// ⭐混合使用Entity和Primitive
function createMixedScene() {
// 创建实体 - 用于交互元素
const pointEntity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(116.39, 39.9),
point: {
pixelSize: 15,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
},
label: {
text: '北京中心',
font: '14px sans-serif',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -10)
}
});
// 创建几何实例 - 用于静态高性能元素
const instances = [];
// 创建1000个随机颜色矩形
for (let i = 0; i < 1000; i++) {
const lon = 116.39 + (Math.random() - 0.5) * 0.5;
const lat = 39.9 + (Math.random() - 0.5) * 0.5;
const width = Math.random() * 500 + 100;
const height = Math.random() * 500 + 100;
instances.push(new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(
lon - 0.005, lat - 0.005,
lon + 0.005, lat + 0.005
),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.fromRandom({alpha: 0.5})
)
},
id: 'rectangle-' + i
}));
}
// 批量添加作为单个primitive
const rectanglesPrimitive = new Cesium.Primitive({
geometryInstances: instances,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: true
})
});
viewer.scene.primitives.add(rectanglesPrimitive);
// 添加拾取支持
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function(click) {
const pickedObject = viewer.scene.pick(click.position);
if (Cesium.defined(pickedObject)) {
if (pickedObject.id === pointEntity) {
console.log('点击了实体:北京中心');
// 处理实体点击
viewer.flyTo(pointEntity);
} else if (typeof pickedObject.id === 'string' && pickedObject.id.startsWith('rectangle-')) {
console.log('点击了矩形:', pickedObject.id);
// 处理矩形点击
const primitiveIndex = parseInt(pickedObject.id.split('-')[1]);
// 可以在这里处理primitive
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
return {
entity: pointEntity,
primitive: rectanglesPrimitive,
handler: handler
};
}
// 创建混合场景
const mixedScene = createMixedScene();
2. 优化策略 - 选择性使用
// ⭐根据数据量动态选择Entity或Primitive
function createOptimizedObjects(positions, options = {}) {
const threshold = options.threshold || 1000; // 阈值,超过则使用Primitive
if (positions.length <= threshold) {
// 数据量小,使用Entity
console.log(`使用Entity API创建${positions.length}个对象`);
const entities = [];
for (let i = 0; i < positions.length; i++) {
const position = positions[i];
const entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
position[0], position[1], position[2] || 0
),
point: {
pixelSize: options.pixelSize || 10,
color: Cesium.Color.BLUE,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
}
});
entities.push(entity);
}
return {
type: 'entity',
objects: entities
};
} else {
// 数据量大,使用Primitive
console.log(`使用Primitive API创建${positions.length}个对象`);
const positionsArray = [];
const colorBytes = [];
// 准备几何数据
for (let i = 0; i < positions.length; i++) {
const cartesian = Cesium.Cartesian3.fromDegrees(
positions[i][0], positions[i][1], positions[i][2] || 0
);
positionsArray.push(cartesian.x, cartesian.y, cartesian.z);
// 使用蓝色
colorBytes.push(0, 0, 255, 255);
}
// 创建点primitive
const pointsPrimitive = createPointsPrimitive(positionsArray, colorBytes);
return {
type: 'primitive',
objects: pointsPrimitive
};
}
}
// 测试不同数据量
const smallPositions = [];
const largePositions = [];
// 生成小数据集
for (let i = 0; i < 100; i++) {
smallPositions.push([
116.39 + (Math.random() - 0.5) * 0.1,
39.9 + (Math.random() - 0.5) * 0.1
]);
}
// 生成大数据集
for (let i = 0; i < 10000; i++) {
largePositions.push([
116.39 + (Math.random() - 0.5) * 0.1,
39.9 + (Math.random() - 0.5) * 0.1
]);
}
// 测试
const smallResult = createOptimizedObjects(smallPositions);
const largeResult = createOptimizedObjects(largePositions);
console.log('小数据集使用:', smallResult.type);
console.log('大数据集使用:', largeResult.type);
🔄 Primitive绘制管理器
// ⭐创建Primitive绘制管理器 - 结合Entity和Primitive优势
class PrimitiveDrawingManager {
constructor(viewer) {
this.viewer = viewer;
this.scene = viewer.scene;
this.primitives = this.scene.primitives;
this.entities = viewer.entities;
// 存储临时绘制实体
this.activeEntity = null;
// 存储已创建的primitives
this.drawnPrimitives = [];
// 临时点集合
this.points = [];
// 事件处理器
this.handler = new Cesium.ScreenSpaceEventHandler(this.scene.canvas);
// 绘制状态
this.drawingMode = null;
this.isDrawing = false;
}
// 开始绘制模式
startDrawing(mode) {
// 结束之前的绘制
this.endDrawing();
this.drawingMode = mode;
this.isDrawing = true;
this.points = [];
// 设置鼠标样式
this.viewer.canvas.style.cursor = 'crosshair';
// 设置点击事件
this.handler.setInputAction(this.handleClick.bind(this), Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 设置移动事件 - 实时预览
this.handler.setInputAction(this.handleMouseMove.bind(this), Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 设置双击事件 - 完成绘制
this.handler.setInputAction(this.handleDoubleClick.bind(this), Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
console.log(`开始${mode}绘制模式`);
}
// 处理点击事件
handleClick(click) {
if (!this.isDrawing) return;
// 拾取点位置
const ray = this.viewer.camera.getPickRay(click.position);
const cartesian = this.scene.globe.pick(ray, this.scene);
if (!cartesian) return;
// 添加点到集合
this.points.push(cartesian);
// 添加点标记 - 使用Entity
this.entities.add({
position: cartesian,
point: {
pixelSize: 8,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 1
}
});
// 更新临时实体
this.updateActiveEntity();
}
// 处理鼠标移动
handleMouseMove(movement) {
if (!this.isDrawing || this.points.length === 0) return;
// 获取鼠标位置
const ray = this.viewer.camera.getPickRay(movement.endPosition);
const cartesian = this.scene.globe.pick(ray, this.scene);
if (!cartesian) return;
// 更新临时实体
this.updateActiveEntity(cartesian);
}
// 处理双击 - 完成绘制
handleDoubleClick(click) {
if (!this.isDrawing) return;
// 转换为Primitive并清理临时实体
this.finishDrawing();
}
// 更新临时绘制实体
updateActiveEntity(currentPosition) {
// 移除之前的临时实体
if (this.activeEntity) {
this.entities.remove(this.activeEntity);
}
if (this.points.length === 0) return;
// 创建临时几何体
const positions = [...this.points];
if (currentPosition) {
positions.push(currentPosition);
}
// 根据绘制模式创建不同实体
switch (this.drawingMode) {
case 'polyline':
this.activeEntity = this.entities.add({
polyline: {
positions: positions,
width: 3,
material: Cesium.Color.YELLOW,
clampToGround: true
}
});
break;
case 'polygon':
if (positions.length >= 3) {
this.activeEntity = this.entities.add({
polygon: {
hierarchy: new Cesium.PolygonHierarchy(positions),
material: Cesium.Color.YELLOW.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.BLACK
}
});
} else {
this.activeEntity = this.entities.add({
polyline: {
positions: [...positions, positions[0]],
width: 3,
material: Cesium.Color.YELLOW,
clampToGround: true
}
});
}
break;
case 'rectangle':
if (positions.length >= 2) {
// 创建矩形
const rect = this.calculateRectangle(positions[0], positions[1]);
this.activeEntity = this.entities.add({
rectangle: {
coordinates: rect,
material: Cesium.Color.YELLOW.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.BLACK
}
});
}
break;
}
}
// 计算矩形坐标
calculateRectangle(p1, p2) {
const cart1 = Cesium.Cartographic.fromCartesian(p1);
const cart2 = Cesium.Cartographic.fromCartesian(p2);
const west = Math.min(cart1.longitude, cart2.longitude);
const east = Math.max(cart1.longitude, cart2.longitude);
const south = Math.min(cart1.latitude, cart2.latitude);
const north = Math.max(cart1.latitude, cart2.latitude);
return new Cesium.Rectangle(west, south, east, north);
}
// 完成绘制并转为Primitive
finishDrawing() {
if (this.points.length < 2) {
this.endDrawing();
return;
}
// 转换为Primitive
let primitive;
switch (this.drawingMode) {
case 'polyline':
primitive = this.createPolylinePrimitive(this.points);
break;
case 'polygon':
primitive = this.createPolygonPrimitive(this.points);
break;
case 'rectangle':
const rect = this.calculateRectangle(this.points[0], this.points[1]);
primitive = this.createRectanglePrimitive(rect);
break;
}
if (primitive) {
this.drawnPrimitives.push(primitive);
console.log(`创建${this.drawingMode} Primitive, 总数: ${this.drawnPrimitives.length}`);
}
// 清理临时实体
this.endDrawing();
}
// 结束绘制并清理
endDrawing() {
// 移除临时实体
if (this.activeEntity) {
this.entities.remove(this.activeEntity);
this.activeEntity = null;
}
// 移除临时点
this.entities.removeAll();
// 重置状态
this.isDrawing = false;
this.points = [];
// 重置事件处理器
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
// 重置鼠标样式
this.viewer.canvas.style.cursor = 'default';
}
// 创建折线Primitive
createPolylinePrimitive(positions) {
const material = Cesium.Material.fromType('Color', {
color: Cesium.Color.YELLOW
});
const polyline = new Cesium.PolylineGeometry({
positions: positions,
width: 3.0,
vertexFormat: Cesium.PolylineMaterialAppearance.VERTEX_FORMAT
});
const instance = new Cesium.GeometryInstance({
geometry: polyline,
id: 'polyline-' + Date.now()
});
const primitive = new Cesium.Primitive({
geometryInstances: instance,
appearance: new Cesium.PolylineMaterialAppearance({
material: material
})
});
this.scene.primitives.add(primitive);
return primitive;
}
// 创建多边形Primitive
createPolygonPrimitive(positions) {
const polygonHierarchy = new Cesium.PolygonHierarchy(positions);
const polygon = new Cesium.PolygonGeometry({
polygonHierarchy: polygonHierarchy,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
const instance = new Cesium.GeometryInstance({
geometry: polygon,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.YELLOW.withAlpha(0.5)
)
},
id: 'polygon-' + Date.now()
});
const primitive = new Cesium.Primitive({
geometryInstances: instance,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: true
})
});
this.scene.primitives.add(primitive);
return primitive;
}
// 创建矩形Primitive
createRectanglePrimitive(rectangle) {
const rect = new Cesium.RectangleGeometry({
rectangle: rectangle,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
const instance = new Cesium.GeometryInstance({
geometry: rect,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.YELLOW.withAlpha(0.5)
)
},
id: 'rectangle-' + Date.now()
});
const primitive = new Cesium.Primitive({
geometryInstances: instance,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: true
})
});
this.scene.primitives.add(primitive);
return primitive;
}
// 清除所有primitive
clearAll() {
this.endDrawing();
for (const primitive of this.drawnPrimitives) {
this.scene.primitives.remove(primitive);
}
this.drawnPrimitives = [];
console.log('已清除所有图形');
}
}
// 使用绘制管理器
const drawingManager = new PrimitiveDrawingManager(viewer);
// 创建绘制控制按钮
function createDrawingUI() {
const container = document.createElement('div');
container.className = 'drawing-controls';
container.style.position = 'absolute';
container.style.top = '10px';
container.style.left = '10px';
container.style.zIndex = '1000';
container.style.backgroundColor = 'rgba(40, 40, 40, 0.8)';
container.style.color = 'white';
container.style.padding = '10px';
container.style.borderRadius = '5px';
const buttons = [
{ name: '画线', mode: 'polyline' },
{ name: '多边形', mode: 'polygon' },
{ name: '矩形', mode: 'rectangle' },
{ name: '清除', action: 'clear' }
];
buttons.forEach(btn => {
const button = document.createElement('button');
button.textContent = btn.name;
button.style.margin = '5px';
button.style.padding = '5px 10px';
button.addEventListener('click', () => {
if (btn.action === 'clear') {
drawingManager.clearAll();
} else {
drawingManager.startDrawing(btn.mode);
}
});
container.appendChild(button);
});
document.body.appendChild(container);
}
// 添加UI
createDrawingUI();
🧩 Entity与Primitive协同使用最佳实践
-
选择策略:
- Entity: 用于交互元素、动态变化的对象、小量数据
- Primitive: 用于静态背景、大量数据、性能敏感场景
-
数据量阈值:
- <500: 首选Entity - 开发便捷,后期便于交互
- 500-5000: 根据交互需求选择
-
5000: 首选Primitive - 性能更佳
-
混合使用推荐:
- 静态背景层用Primitive
- 交互前景层用Entity
- 临时绘制对象用Entity
- 持久化存储的形状用Primitive
-
性能优化:
- 合并相似材质的Primitive
- 使用批处理渲染(Batch)
- 动态调整可见性而非创建/销毁
-
高级协同技术:
- 使用Entity绘制,转换为Primitive存储
- 大数据集自动降级到Primitive
- 结合Entity拾取与Primitive渲染
🧩 记忆助手
选择指南: Entity适合交互与少量数据,Primitive适合性能与大量数据
协同技巧: 用Entity画草图,用Primitive做成品
性能黄金法则: 可见性控制 > 批量处理 > 延迟创建
2138

被折叠的 条评论
为什么被折叠?



