之前学过、用过cesium,没有系统化的总结,现在整理一下,方便自己,也方便他人。
本文主要介绍Primitive绘制图形的用法,暂不涉及深层次的原理,等自己有时间了,可以尝试读一读源码,再写一些体会。
1.简单介绍
Primitive其实是 一种比较复杂的、面向图形开发人员的类。Primitive绘制图形的方式更接近渲染引擎底层。它绘制的图形有2部分组成:
1.Geometry,几何形状,定义Primitive图形的结构,如面、椭圆、线条等。
2.Appearance,外观,定义Primitive图形的渲染着色,通俗来讲,就是定义Primitive图形的外观材质。
在Primitive中,几何实例(Geometry)可以有一个或者多个,但是外观(Appearance)只能有一个,在使用的时候需要注意。
常用的几何图形如下所示:
| 几何形状 | 用途描述 |
|---|---|
| PolylineGeometry | 可以具有一定宽度的多段线 |
| PolygonGeometry | 可以具有空洞或者拉伸高度的多边形 |
| EllipseGeometry | 椭圆或者拉伸的椭圆 |
| CircleGeometry | 圆或者拉伸的圆 |
| CorridorGeometry | 走廊,沿地表且具有一定宽度 |
| RectangleGeometry | 矩形或者拉伸的矩形 |
| WallGeometry | 具有一定高度的墙 |
| BoxGeometry | 以原点为中心点的立方体 |
| EllipsoidGeometry | 以原点为中心点的椭球体 |
| CylinderGeometry | 圆柱体或圆锥体 |
| SphereGeometry | 以原点为中心点的球体 |
常用的几何外观如下所示
| 几何外观 | 用途描述 |
|---|---|
| MaterialAppearance | 任意几何外观,支持使用材质着色 |
| EllipsoidSurfaceAppearance | 椭球体表面的几何外观,不支持几何 |
| PerlnstanceColorAppearance | 让几何形状使用自定义颜色着色 |
| PolylineMaterialAppearance | 多线段的几何外观,支持使用材质着 |
| PolylineColorAppearance | 使用每个片段或顶点的颜色来着色多线段 |
创建primitive,先指定几何形状, 在指定外观,搭配起来,就完成了图形的绘制,但也不是任何一个形状和外观都可以搭配的,这个在实际使用中要注意。
2.绘制图形
(1)线 polyline
在定义几何形状时候,需要先new一个Cesium.GeometryInstance 几何实例,然后再去指定几何形状是什么。
//绘制线
//定义几何形状
var polyline = new Cesium.GeometryInstance({
geometry: new Cesium.PolylineGeometry({
positions: Cesium.Cartesian3.fromDegreesArray([
108.0, 31.0, 100.0, 36.0, 105.0, 39.0,
]),
width: 5.0,
}),
});
//定义外观
var polylineAppearance = new Cesium.PolylineMaterialAppearance({
material: Cesium.Material.fromType("Color", {
color: new Cesium.Color(1.0, 1.0, 0.0, 1.0),
}),
});
//创建Primitive
var addPolylineGeometry = new Cesium.Primitive({
geometryInstances: polyline,
appearance: polylineAppearance,
});
viewer.scene.primitives.add(addPolylineGeometry);
(2)多边形面 PolygonGeometry
//绘制面
//定义几何形状
var polygon = new Cesium.GeometryInstance({
geometry: new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray([
108, 45, 109, 48, 104, 48, 103, 45,
])
),
}),
});
//定义外观
var polygonAppearance = new Cesium.MaterialAppearance({
material: Cesium.Material.fromType("Dot"),
});
//创建Primitive
var addPolygonGeometry = new Cesium.Primitive({
geometryInstances: polygon,
appearance: polygonAppearance,
});
viewer.scene.primitives.add(addPolygonGeometry);
(3) 椭圆 EllipseGeometry
椭圆,用到了条纹材质
//绘制椭圆
//定义几何形状
var ellipse = new Cesium.GeometryInstance({
geometry: new Cesium.EllipseGeometry({
center: Cesium.Cartesian3.fromDegrees(105, 40.0),
semiMajorAxis: 500000.0,
semiMinorAxis: 300000.0,
//rotation: Cesium.Math.toRadians(60.0)
}),
});
//定义外观
var ellipseAppearance = new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType("Stripe"),//条纹
});
//创建Primitive
var addEllipseGeometry = new Cesium.Primitive({
geometryInstances: ellipse,
appearance: ellipseAppearance,
});
viewer.scene.primitives.add(addEllipseGeometry);
(4)圆 CircleGeometry
圆形,用上了网格材质
//绘制圆
//定义几何形状
var circle = new Cesium.GeometryInstance({
geometry: new Cesium.CircleGeometry({
center: Cesium.Cartesian3.fromDegrees(100, 45.0),
radius: 300000.0,
}),
});
//定义外观
var circleAppearance = new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType("Grid"),//网格
});
//创建Primitive
var addCircleGeometry = new Cesium.Primitive({
geometryInstances: circle,
appearance: circleAppearance,
});
viewer.scene.primitives.add(addCircleGeometry);
(5)走廊 CorridorGeometry
var corridor = new Cesium.GeometryInstance({
geometry: new Cesium.CorridorGeometry({
positions: Cesium.Cartesian3.fromDegreesArray([
100.0, 40.0, 105.0, 35.0, 102.0, 33.0,
]),
width: 100000,
}),
attributes: {
//颜色,红绿蓝,透明度
color: new Cesium.ColorGeometryInstanceAttribute(
0.8,
0.5,
0.8,
0.7
),
},
});
//定义外观
var corridorAppearance = new Cesium.PerInstanceColorAppearance({
flat: true, //外观是平的
translucent: true, //半透明的
});
//创建Primitive
var addCorridorGeometry = new Cesium.Primitive({
geometryInstances: corridor,
appearance: corridorAppearance,
});
viewer.scene.primitives.add(addCorridorGeometry);
(6)矩形 RectangleGeometry
//绘制矩形
//定义几何形状
var rectangle = new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(
95.0,
39.0,
100.0,
42.0
),
// height: 10000.0,
}),
});
//定义外观
var rectangleAppearance = new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType("Water"),
});
//创建Primitive
var addRectangleGeometry = new Cesium.Primitive({
geometryInstances: rectangle,
appearance: rectangleAppearance,
});
viewer.scene.primitives.add(addRectangleGeometry);
(7)墙 WallGeometry
//绘制墙
//定义几何形状
var wall = new Cesium.GeometryInstance({
geometry: new Cesium.WallGeometry({
positions: Cesium.Cartesian3.fromDegreesArrayHeights([
107.0, 43.0, 100000.0, 97.0, 43.0, 100000.0, 97.0, 40.0,
100000.0, 107.0, 40.0, 100000.0, 107.0, 43.0, 100000.0,
]),
}),
});
//定义外观
var wallAppearance = new Cesium.MaterialAppearance({
material: Cesium.Material.fromType("Color"),
});
//创建Primitive
var addWallGeometry = new Cesium.Primitive({
geometryInstances: wall,
appearance: wallAppearance,
});
viewer.scene.primitives.add(addWallGeometry);
除了绘制这些基本的图形外,primitive还可以绘制 立方体,圆柱体,椭球体等,其中会涉及到矩阵变换、四元数等概念,这些概念比较抽象,这里先留个坑,后续等我有了更深刻的理解,再专门写文章聊一聊。
3.primitive 绘制贴地
entity可以贴地绘制图形,primitive也是可以的,答案就是GroundPrimitive。
对于CircleGeometry(圆)、CorridorGeometry(走廊)、EllipseGeometry(椭圆)、PolygonGeometry(多边形)和RectangleGeometry(矩形)等几何图形,Cesium提供了GroundPrimitive类,可以实现贴地。
对于线,
Cesium提供了专门的贴地线GroundPolylinePrimitive和相对应的贴地线几何图形 GroundPolylineGeometry。
其他方面和primitive绘制图形一样,没有什么变化。
primitive绘制贴地图形还有2个要求:
1.GroundPrimitive中的几何图形必须来自单个几何图形,目前还不支持对多个几何图形进行批处理。
2.不支持对立体几何图形的贴地。
(1)线 贴地
重点是,
geometry: new Cesium.GroundPolylineGeometry
new Cesium.GroundPolylinePrimitive
//绘制线
//定义几何形状
var polyline = new Cesium.GeometryInstance({
geometry: new Cesium.GroundPolylineGeometry({
//贴地线
positions: Cesium.Cartesian3.fromDegreesArray([
108.0, 31.0, 100.0, 36.0, 105.0, 39.0,
]),
width: 5.0,
}),
});
//定义外观
var polylineAppearance = new Cesium.PolylineMaterialAppearance({
material: Cesium.Material.fromType("Color"),
});
//创建GroundPrimitive
var addGroundPolylinePrimitive = new Cesium.GroundPolylinePrimitive(
{
//仅支持GroundPolylineGeometry
geometryInstances: polyline,
appearance: polylineAppearance,
}
);
viewer.scene.primitives.add(addGroundPolylinePrimitive);
(2)面 贴地
new Cesium.GroundPrimitive
//绘制面
//定义几何形状
var polygon = new Cesium.GeometryInstance({
geometry: new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray([
110.0, 30.0, 114.0, 38.0, 106.0, 35.0, 108.0, 30.0,
])
),
}),
});
//定义外观
var polygonAppearance = new Cesium.MaterialAppearance({
material: Cesium.Material.fromType("Color", {
color: new Cesium.Color(0.5, 0.8, 0.0, 0.6),
}),
faceForward: true,
});
//创建GroundPrimitive
var addPolygonGroundPrimitive = new Cesium.GroundPrimitive({
//贴地面
geometryInstances: polygon,
appearance: polygonAppearance,
});
viewer.scene.primitives.add(addPolygonGroundPrimitive);
(3)其他贴地
对于CircleGeometry(圆)、CorridorGeometry(走廊)、EllipseGeometry(椭圆)、PolygonGeometry(多边形)和RectangleGeometry(矩形),他们的贴地方式都是一样的,需要在实例化primitive 的时候,使用GroundPrimitive,这里就不再赘述。
4.primitive 管理
(1)合并绘制
一个primitive,由2部分组成,
1.几何实例
2.外观
几何实例可以有一个或多个,外观只有一个,当有多个几何实例的时候,就可以称之为合并绘制。
这里我们绘制2个矩形实例,共用一个外观,绘制一下:
// 创建几何体实例1
let instance1 = new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(115, 20, 135, 30),
// 距离表面高度
height: 0,
vertexFormat:
Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.RED.withAlpha(0.5)
),
},
});
// 创建几何体实例2
let instance2 = new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(140, 20, 160, 30),
height: 0,
vertexFormat:
Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.BLUE.withAlpha(0.5)
),
},
});
// 03-设置外观
let appearance = new Cesium.PerInstanceColorAppearance({
flat: true,
});
// 04-实例化primitive,两个实例,1个外观
let primitive = new Cesium.Primitive({
geometryInstances: [instance1, instance2],
appearance: appearance,
});
// 05-添加到viewer
viewer.scene.primitives.add(primitive);
我们还可以玩一个更厉害的,直接绘制绘制2592个铺满整个地球的半透明、颜色随机的矩形。代码如下:
//合并多个矩形
var instances = []; //存储几何实例
for (var lon = -180.0; lon < 180.0; lon += 5.0) {
for (var lat = -85.0; lat < 85.0; lat += 5.0) {
instances.push(
new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(
lon,
lat,
lon + 5.0,
lat + 5.0
),
vertexFormat:
Cesium.PerInstanceColorAppearance
.VERTEX_FORMAT,
}),
id: lon + "-" + lat,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.fromRandom({
alpha: 0.6,
})
),
},
})
);
}
}
//创建Primitive
var mergeInstances = new Cesium.Primitive({
geometryInstances: instances,
appearance: new Cesium.PerInstanceColorAppearance(),
});
viewer.scene.primitives.add(mergeInstances);
通过循环嵌套,以经纬度5度为增量,绘制出两千多个小矩形,他们都是几何实例,存储在数组中,最终通过primitive绘制出来。
在cesium中,绘制大量图形时,用primitive是很有优势的(以下4句话来自chatGPT):
- 性能优势:
Primitive是Cesium中的一种渲染机制,专门用于绘制大量的几何图形。它在渲染大量图形时比较高效,能够更好地处理复杂的场景,而不会影响帧率。 - 批量处理:
Primitive允许您将多个几何实例组合成一个渲染批次,从而减少了渲染调用的次数。这有助于减少CPU和GPU之间的数据传输,提高了渲染效率。 - 自动优化:Cesium的
Primitive会自动根据视野和距离进行优化,只渲染那些在视野范围内的图形,从而减少不必要的渲染负担。 - 内存优化:由于
Primitive是针对大量图形设计的,它能够更有效地管理内存,减少不必要的资源占用。
(2)拾取
Cesium中有多种拾取方法,其中,Scene类中的Pick方法用于拾取指定位置顶端的一个Primitive属性对象。我们可以通过该方法来获取几何图形,并对其进行某些操作。
现在我们就实现一个功能,当我们点击某个几何图形的时候,修改它的颜色为黄色。
下面这段代码要在上面代码的基础上使用,假定你已经使用了上面的代码,生成了2千多个矩形。
var handler = new Cesium.ScreenSpaceEventHandler(
viewer.scene.canvas
);
//监听鼠标左键点击事件
handler.setInputAction(function (movement) {
//获取鼠标点击位置的实例
var pick = viewer.scene.pick(movement.position);
//被点击的实例变为黄色
if (Cesium.defined(pick) && typeof pick.id == "string") {
let attributes =
mergeInstances.getGeometryInstanceAttributes(pick.id);
attributes.color =
Cesium.ColorGeometryInstanceAttribute.toValue(
Cesium.Color.YELLOW.withAlpha(0.9)
);
}
//弹窗展示被点击的实例ID
alert("点击实例id为:" + pick.id);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
当我们点击某个矩形,弹窗+变色
多点击几个位置
(3)修改属性
实际上,修改属性已经在上面的例子中用过了,先通过id获取实例属性,再修改
let attributes =
mergeInstances.getGeometryInstanceAttributes(pick.id);
attributes.color =
Cesium.ColorGeometryInstanceAttribute.toValue(
Cesium.Color.YELLOW.withAlpha(0.9)
);
(4)移除
移除指定的peimitive对象
viewer.scene.primitives.remove(addCylinderGeometry)
移除全部的primitive对象
viewer.scene.primitives.removeAll()