Cesium中通過 ??Primitive 高效添加 ??點、線、多邊形、圓、橢圓、球、模型?? 等地理要素,以下是各類地理要素的高效添加方式:
一、公告板
1. 創建 BillboardCollection 并添加到場景?
const billboards = viewer.scene.primitives.add(new Cesium.BillboardCollection());
- ??new Cesium.BillboardCollection()??
創建一個新的 ??公告板集合??(BillboardCollection),用于管理多個公告板(Billboard)。
- ??viewer.scene.primitives.add(...)??
將這個公告板集合添加到 Cesium 的 ??場景(Scene)?? 中,使其能夠被渲染。
作用??:
類似于創建一個“容器”,用于高效管理多個公告板(比如多個圖標、標記點等),而不是單獨添加每個公告板(單獨添加會有更高的性能開銷)。?
2. 向 BillboardCollection 添加一個公告板??
billboards.add({position: Cesium.Cartesian3.fromDegrees(113.3244, 23.1049, 600),image: '/Assets/nav.svg',width: 32,height: 32,scaleByDistance: new Cesium.NearFarScalar(1e3, 1.0, 2e6, 0.2)
});
- ??position: Cesium.Cartesian3.fromDegrees(113.3244, 23.1049, 600)??
定義公告板的 ??3D 坐標位置??:
113.3244, 23.1049 是經緯度(WGS84 坐標系)。
600 是高度(單位:米),表示公告板在地球表面上方 600 米處。
- ??image: '/Assets/nav.svg'??
公告板顯示的 ??圖片路徑??(這里是 /Assets/nav.svg,可以是一個 SVG 或 PNG 圖標)。
- ??width: 32, height: 32??
公告板的 ??尺寸??(寬度和高度均為 32 像素)。
- ??scaleByDistance: new Cesium.NearFarScalar(1e3, 1.0, 2e6, 0.2)??
??視距縮放優化??(根據相機距離動態調整公告板大小):
當相機距離 ??1000 米(1e3)?? 時,公告板顯示原始大小(1.0 倍)。
當相機距離 ??2000000 米(2e6)?? 時,公告板縮小到 0.2 倍(避免遠處圖標過大)。
作用??:
在指定位置(廣州附近,高度 600 米)添加一個 ??導航圖標??(nav.svg),并優化其顯示大小(近大遠小)。?
3、整體作用??
這段代碼的 ??核心功能?? 是:
- ??創建一個公告板集合??(BillboardCollection),用于高效管理多個公告板。
- ??向集合中添加一個公告板??,指定其:
- ??位置??(經緯度 + 高度)。
- ??顯示的圖片??(nav.svg)。
- ??尺寸??(32x32 像素)。
- ??視距縮放優化??(近大遠小,避免遠處圖標過大)。
4、優化點??
- ??使用 BillboardCollection 而不是單獨添加 Billboard??
批量管理多個公告板時,性能更高(減少 GPU 調用次數)。
- ??scaleByDistance 優化??
避免遠處圖標過大,提升視覺效果。
- ??支持 3D 位置(含高度)??
不僅能在地表放置圖標,還能在 3D 空間(如空中)放置。
5、擴展用法??
如果需要添加多個公告板,可以循環調用 billboards.add():
const positions = [{ lon: 113.3244, lat: 23.1049, height: 600 },{ lon: 113.3254, lat: 23.1059, height: 700 }
];
positions.forEach(pos => {billboards.add({position: Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, pos.height),image: '/Assets/nav.svg',width: 32,height: 32});
});
性能更好的方式:
const billboards = viewer.scene.primitives.add(new Cesium.BillboardCollection({scene: viewer.scene,debugShowBoundingVolume: false // 關閉調試框,提升性能[4](@ref)})
);const positions = [{ lon: 116.40, lat: 39.91, image: "icon1.png" },{ lon: 121.47, lat: 31.23, image: "icon2.png" }
];const billboardList = positions.map(pos => {return {image: pos.image,position: Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat),scale: 0.8,color: Cesium.Color.WHITE.withAlpha(0.9),horizontalOrigin: Cesium.HorizontalOrigin.CENTER,verticalOrigin: Cesium.VerticalOrigin.BOTTOM};
});// 批量添加(減少渲染調用)
billboards.add(billboardList);
6、動態更新策略
直接修改屬性
const billboard = billboards.get(0); // 獲取第一個廣告牌
billboard.scale = 1.2; // 修改縮放比例
billboard.position = Cesium.Cartesian3.fromDegrees(120.0, 30.0); // 更新位置
動態效果(旋轉/閃爍)???
// 通過 preRender 事件實現旋轉動畫[9](@ref)
viewer.scene.preRender.addEventListener(() => {const time = Date.now() * 0.001;billboard.rotation = time % (Math.PI * 2); // 持續旋轉billboard.color.alpha = 0.5 + 0.5 * Math.sin(time); // 透明度閃爍
});
按需更新??
// 僅當廣告牌可見時更新
if (billboard.show) {billboard.scale = calculateScaleBasedOnDistance();
}
7、性能優化技巧
7.1 ??GPU 合并渲染??
? ? ??批量添加??:單次 billboards.add() 提交多個廣告牌,觸發 GPU 實例化渲染。
? ? ??紋理復用??:相同圖片自動合并紋理,減少 Draw Call。
7.2 距離動態控制??
billboard.scaleByDistance = new Cesium.NearFarScalar(1e3, 1.0, 1e5, 0.2);
billboard.translucencyByDistance = new Cesium.NearFarScalar(1e4, 1.0, 2e5, 0.1);
近距離正常顯示,遠距離縮小并漸隱,降低渲染負載。?
7.3 ??視錐體裁剪??
viewer.scene.frustumCulling = true; // 默認開啟,自動剔除視野外廣告牌
8、內存管理機制
8.1 移除單個廣告牌??
billboards.remove(billboard); // 移除指定對象
8.2 ??批量清理??
// 移除所有廣告牌
billboards.removeAll();
// 或從場景中移除整個集合
viewer.scene.primitives.remove(billboards);
8.3 避免內存泄漏??
// 銷毀時釋放資源
viewer.scene.primitives.destroyPrimitives = true;
9、總結?
代碼部分 | 作用 |
---|---|
new Cesium.BillboardCollection() | 創建公告板集合(高效管理多個 Billboard) |
viewer.scene.primitives.add(...) | 將集合添加到場景(使其可渲染) |
billboards.add({...}) | 添加一個公告板,指定位置、圖片、尺寸和縮放優化 |
這段代碼是 ??Cesium 中高效添加和管理 3D 圖標/標記的標準方式??,適用于地圖、仿真、游戲等場景。?
二、文本
使用 Primitive API
// 初始化Viewerconst viewer = new Cesium.Viewer('cesiumContainer', {terrainProvider: Cesium.createWorldTerrain(),sceneMode: Cesium.SceneMode.SCENE3D});// 創建LabelCollection圖元const labelCollection = viewer.scene.primitives.add(new Cesium.LabelCollection({show: true,// 啟用深度測試避免被地形遮擋(需權衡性能)depthTest: false}));// 批量添加文本標簽const positions = [{ lon: 116.404, lat: 39.915, text: "北京" },{ lon: 121.47, lat: 31.23, text: "上海" },// 更多位置數據...];positions.forEach(pos => {labelCollection.add({position: Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat),text: pos.text,font: '14px sans-serif', // 字體優化:避免過大字號fillColor: Cesium.Color.WHITE,outlineColor: Cesium.Color.BLACK,outlineWidth: 2,// 垂直對齊:文本位于坐標點下方verticalOrigin: Cesium.VerticalOrigin.BOTTOM,// 像素偏移:微調位置pixelOffset: new Cesium.Cartesian2(0, -15)});});
三. 點(Point)??
使用 Primitive API(高性能,適合大量點)?
const pointPrimitiveCollection = viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection());
pointPrimitiveCollection.add({position: Cesium.Cartesian3.fromDegrees(113.3244, 23.1049, 0),color: Cesium.Color.RED,pixelSize: 10
});
四、線(Polyline)?
使用 Primitive API(Primitive 方式需手動構建 Geometry)?
const polylineCollection = viewer.scene.primitives.add(new Cesium.PolylineCollection());
polylineCollection.add({positions: Cesium.Cartesian3.fromDegreesArray([113.3244, 23.1049,113.3254, 23.1059]),width: 2,material: new Cesium.ColorMaterialProperty(Cesium.Color.BLUE)
});
五、多邊形(Polygon)?
使用 Primitive API(Primitive 方式需手動構建 Geometry)?
const polygonCollection = viewer.scene.primitives.add(new Cesium.PolygonCollection());
polygonCollection.add({hierarchy: Cesium.Cartesian3.fromDegreesArray([113.3244, 23.1049,113.3254, 23.1059,113.3264, 23.1039]),material: new Cesium.ColorMaterialProperty(Cesium.Color.GREEN.withAlpha(0.5))
});
六、圓(Circle)?
使用 Primitive API(需手動計算圓周點)?
// 初始化Cesium
const viewer = new Cesium.Viewer('cesiumContainer', {terrainProvider: Cesium.createWorldTerrain(),baseLayerPicker: false, // 禁用底圖選擇器geocoder: false, // 禁用地理編碼器homeButton: false, // 禁用主頁按鈕infoBox: false, // 禁用信息框sceneModePicker: false, // 禁用場景模式選擇器selectionIndicator: false, // 禁用選擇指示器navigationHelpButton: false, // 禁用導航幫助按鈕animation: false, // 禁用動畫控件timeline: false, // 禁用時間軸fullscreenButton: false // 禁用全屏按鈕
});// 定義圓的中心點和半徑
const centerLon = 113.3244;
const centerLat = 23.1049;
const radiusInMeters = 1000;// 將圓心轉換為 Cartesian3 坐標
const centerCartesian = Cesium.Cartesian3.fromDegrees(centerLon, centerLat);// 計算圓的 Cartesian3 點集(近似采樣)
const granularity = Cesium.Math.RADIANS_PER_DEGREE; // 采樣精度(弧度/度)
const positions = [];
for (let angle = 0; angle < 360; angle += granularity) {const radians = Cesium.Math.toRadians(angle);// 計算圓周上的點(基于球面坐標)const x = radiusInMeters * Math.cos(radians);const y = radiusInMeters * Math.sin(radians);// 將局部坐標轉換為全局 Cartesian3const point = Cesium.Cartesian3.fromDegrees(centerLon + x / 111320, // 經度偏移(1度≈111320米)centerLat + y / (111320 * Math.cos(Cesium.Math.toRadians(centerLat))), // 緯度偏移(考慮緯度縮放)0 // 高度(與圓心相同));positions.push(point);
}// 閉合圓(首尾相連)
positions.push(positions[0]);// 使用 Primitive API 添加圓
const primitiveCollection = viewer.scene.primitives.add(new Cesium.PrimitiveCollection());
primitiveCollection.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: new Cesium.PolygonGeometry({polygonHierarchy: new Cesium.PolygonHierarchy(positions),perPositionHeight: false // 固定高度}),attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE.withAlpha(0.5) // 半透明藍色)}}),appearance: new Cesium.PerInstanceColorAppearance({outline: true,outlineColor: Cesium.Color.RED,outlineWidth: 2})})
);// 定位相機到圓的位置
viewer.camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 5000),orientation: {heading: Cesium.Math.toRadians(0),pitch: Cesium.Math.toRadians(-30)}
});
七、橢圓(Ellipse)?
使用 Primitive API(需手動計算橢圓點集)?
const viewer = new Cesium.Viewer('cesiumContainer', {terrainProvider: Cesium.createWorldTerrain(),baseLayerPicker: false, // 禁用底圖選擇器geocoder: false, // 禁用地理編碼器homeButton: false, // 禁用主頁按鈕infoBox: false, // 禁用信息框sceneModePicker: false, // 禁用場景模式選擇器selectionIndicator: false, // 禁用選擇指示器navigationHelpButton: false, // 禁用導航幫助按鈕animation: false, // 禁用動畫控件timeline: false, // 禁用時間軸fullscreenButton: false // 禁用全屏按鈕
});// 定義橢圓的中心點、半長軸、半短軸和旋轉角度const centerLon = 113.3244;const centerLat = 23.1049;const semiMajorAxis = 2000; // 半長軸(米)const semiMinorAxis = 1000; // 半短軸(米)const rotation = Cesium.Math.toRadians(45); // 旋轉角度(弧度)// 將橢圓中心轉換為 Cartesian3 坐標const centerCartesian = Cesium.Cartesian3.fromDegrees(centerLon, centerLat);// 計算橢圓的 Cartesian3 點集(近似采樣)const granularity = Cesium.Math.RADIANS_PER_DEGREE; // 采樣精度(弧度/度)const positions = [];for (let angle = 0; angle < 360; angle += granularity) {const radians = Cesium.Math.toRadians(angle);// 計算橢圓上的點(基于參數方程)const x = semiMajorAxis * Math.cos(radians);const y = semiMinorAxis * Math.sin(radians);// 旋轉橢圓const rotatedX = x * Math.cos(rotation) - y * Math.sin(rotation);const rotatedY = x * Math.sin(rotation) + y * Math.cos(rotation);// 將局部坐標轉換為全局 Cartesian3const point = Cesium.Cartesian3.fromDegrees(centerLon + rotatedX / 111320, // 經度偏移(1度≈111320米)centerLat + rotatedY / (111320 * Math.cos(Cesium.Math.toRadians(centerLat))), // 緯度偏移(考慮緯度縮放)0 // 高度(與橢圓中心相同));positions.push(point);}// 閉合橢圓(首尾相連)positions.push(positions[0]);// 使用 Primitive API 添加橢圓const primitiveCollection = viewer.scene.primitives.add(new Cesium.PrimitiveCollection());primitiveCollection.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: new Cesium.PolygonGeometry({polygonHierarchy: new Cesium.PolygonHierarchy(positions),perPositionHeight: false // 固定高度}),attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE.withAlpha(0.5) // 半透明藍色)}}),appearance: new Cesium.PerInstanceColorAppearance({outline: true,outlineColor: Cesium.Color.RED,outlineWidth: 2})}));// 定位相機到橢圓位置viewer.camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 5000),orientation: {heading: Cesium.Math.toRadians(0),pitch: Cesium.Math.toRadians(-30)}});
八、球(Sphere)?
使用 Primitive API(Primitive 方式需手動構建 Geometry)?
const sphereCollection = viewer.scene.primitives.add(new Cesium.PrimitiveCollection());
sphereCollection.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: new Cesium.EllipsoidGeometry({vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL,radii: new Cesium.Cartesian3(100, 100, 100)})}),appearance: new Cesium.PerInstanceColorAppearance()})
);
九、3D 模型(Model)?
使用 Primitive API(Primitive 方式需手動加載模型)?
性能優化建議?
場景 | 推薦方式 | 原因 |
---|---|---|
??少量要素?? | Entity ?API | 代碼簡潔,開發效率高 |
??大量要素(>1000)?? | Primitive ?API | 性能更高,減少 CPU-GPU 通信開銷 |
??動態更新(如軌跡動畫)?? | Entity ?API | 支持更簡單的屬性動畫 |
??自定義渲染(如特殊著色器)?? | Primitive ?API | 可深度定制渲染邏輯 |
總結?
要素類型 | 推薦 API | 示例代碼 |
---|---|---|
??點?? | Entity ?或?PointPrimitiveCollection | viewer.entities.add({ point: {...} }) |
??線?? | Entity ?或?PolylineCollection | viewer.entities.add({ polyline: {...} }) |
??多邊形?? | Entity ?或?PolygonCollection | viewer.entities.add({ polygon: {...} }) |
??圓/橢圓?? | Entity ?API(更簡單) | viewer.entities.add({ circle: {...} }) |
??球?? | Entity ?API(更簡單) | viewer.entities.add({ ellipsoid: {...} }) |
??模型?? | Entity ?API(更簡單) | viewer.entities.add({ model: {...} }) |