在Cesium中, Entity , Primitive 两个接口是用于绘制几何图形的重要接口,我们在平时开发中会经常调用。
今天我们就来详细了解一下Primitive接口。
PS:当然后续也会聊一聊 DrawCommand 这个接口。
1.Entity与Primitive
这里会有一个疑问,为什么要有两套绘制图形的接口呢?
Entity
是Cesium封装的较高级的绘图接口,适用于普通的开发人员,即使你没有图形开发技术,你能快速使用Entity在场景中绘制各种几何形状。
Primitive
是Cesium封装的低级的绘图接口,它的绘图方式接近渲染引擎底层,但又可以不直接使用WebGL底层的接口,主要面向图形开发人员。
这里也就意味着使用Primitive进行图形绘制时需要具备一定的图形相关开发知识。
2.Primitive的构成
const circle = new Cesium.CircleGeometry({
center: Cesium.Cartesian3.fromDegrees(110.0, 30.0, 500),
radius: 200.0
});
const geometry = Cesium.CircleGeometry.createGeometry(circle);
const instance = new Cesium.GeometryInstance({
geometry: geometry,
});
viewer.scene.primitives.add(new Cesium.Primitive({
geometryInstances: instance,
appearance: new Cesium.MaterialAppearance({
material: new Cesium.Material({
fabric: {
type: "Color",
uniforms: {
color: Cesium.Color.BLUE
}
}
})
})
}));
上面的实例是使用Primitive创建圆并渲染到场景中,通过这个实例我们发现创建一个Primitive必须设置 geometry 与 appear
ance 两个属性,才能在场景中渲染出来。
3.Geometry与Appearance
geometry : 代表要显示的几何形状。
appear ance : 代表该几何的外观样式。
即 geometry 形成一个骨架, appear ance 表示对 geometry 上色。
一个Primitive中可以包含多个Geometry,但是只能有一个Appearance
比如下方的案例代码:
let p = [110.0, 30.0];
let instances = [];
for (let i = 0; i < 10; i++) {
const instance = new Cesium.GeometryInstance({
geometry: new Cesium.EllipseGeometry({
center: Cesium.Cartesian3.fromDegrees(p[0], p[1]),
semiMinorAxis: 2000.0,
semiMajorAxis: 2000.0,
height:1000*i
})
});
instances.push(instance);
}
viewer.scene.primitives.add(new Cesium.Primitive({
geometryInstances: instances,
appearance: new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType('Color')
})
}));
每个Geometry是通过GeometryInstance来进行实例化的,通过GeometryInstance对Geometry进行实例化,同一个Geometry可以被实例化多次。
这对于展示大量数
据时很有用,比如有10万个BoxGeometry需要被显示,我们只需要创建一个BoxGeometry,然后通过GeometryInstance来设置每一个的大小、位置、颜色等。
let p = [110.0, 30.0];
let instances = [];
let boxGeometry = Cesium.BoxGeometry.fromDimensions({
dimensions: new Cesium.Cartesian3(100, 100, 100)
})
for (let i = 0; i < 100000; i++) {
const instance = new Cesium.GeometryInstance({
geometry: boxGeometry,
modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(p[0] + Math.random() , p[1] + Math.random() , 200+Math.random()*100))//通过modelMatrix设置不同的位置
});
instances.push(instance);
}
viewer.scene.primitives.add(new Cesium.Primitive({
geometryInstances: instances,
appearance: new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType('Color')
})
}));
我们还可以通过对GeometryInstance设置一些属性来进行Geometry的识别,当一个Primitive中装载有多个Geometry时,我们在鼠标交互的时候如果希望知道拾取到
的是那个Geometry,那么就可以通过GeometryInstance设置一个id,这样我们就知道拾取到的是哪个Geometry。
let p = [110.0, 30.0];
let instances = [];
let boxGeometry = Cesium.BoxGeometry.fromDimensions({
dimensions: new Cesium.Cartesian3(100, 100, 100)
})
for (let i = 0; i < 100000; i++) {
const instance = new Cesium.GeometryInstance({
geometry: boxGeometry,
modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(p[0] + Math.random(), p[1] + Math.random(), 200 + Math.random() * 100)),
id: "BoxGeometry" + i
});
instances.push(instance);
}
viewer.scene.primitives.add(new Cesium.Primitive({
geometryInstances: instances,
appearance: new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType('Color')
})
}));
new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas).setInputAction(e => {
let pick = viewer.scene.pick(e.position);
console.log(pick);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
需要注意的是,因为一个Primitive只能设置一个Appearance,所以当一个Primitive装载有多个Geometry时,这些Geometry只能具有相同的外观。我们可以通过PerInstanceColorAppearance类型的外观为每个Geometry实例设置一个颜色。
let p = [110.0, 30.0];
let instances = [];
let boxGeometry = Cesium.BoxGeometry.fromDimensions({
dimensions: new Cesium.Cartesian3(100, 100, 100)
})
for (let i = 0; i < 100000; i++) {
const instance = new Cesium.GeometryInstance({
geometry: boxGeometry,
modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(p[0] + Math.random() / 10, p[1] + Math.random() / 10, 200 + Math.random() * 100)),
id:"BoxGeometry" + i,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({ alpha: 1 }))
}
});
instances.push(instance);
}
viewer.scene.primitives.add(new Cesium.Primitive({
geometryInstances: instances,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false
})
}));
4.Primitive的优缺点
优点: 1.灵活度高,因为Primitive由Geometry和Appearance构成,我们可以单独修改它们。
2.性能好。使用Primitive可以将多个Geometry合并为一个大的Geometry来减少CPU的使用,利用GPU。
3.自定义渲染强,我们有很大的可编程性。可以操作顶点着色器与片元着色器。
缺点:
1.对于动态数据不友好,使用primitive组合多个geometry时无法更新geometry。
2.使用复杂。编写代码增加,需要图形学知识。
大家可以使用Entity与Primitive分别去创建100万条数据进行性能测试,对下对比,会发现使用Primitive创建图形编写代码会比Entity方式编写代码量多很多。但是Primitive与Entity渲染出来的效果会相差更多,帧率也会相差很大。
5.Primitive的分类
Cesium中除了Primitive类以外,还有一些以Primitive结尾的类。
1. new Cesium.GroundPrimitive (options)
贴地Primitive,将几何图形贴地使用。适用于平面类型的Geometry,如:CircleGeometry,PolygonGeometry,RectangleGeometry。
贴地Primitive通过设置 classificationType 来设置贴的目标。
2. new Cesium.ClassificationPrimitive (options)
分类Primitive,将几何体贴模型高亮显示,常用于高亮显示模型。比如倾斜摄影分层单体化或者分户单体化。
3 . new Cesium.GroundPolylinePrimitive (options)
贴地线Primitive
4. new Cesium.PointPrimitive ()
通过调用PointPrimitiveCollection#add创建点并设置其初始属性。 不要直接调用构造函数。
5. new Cesium.PointPrimitiveCollection (options)
点集合
6 . new Cesium.VoxelPrimitive (options)
体渲染
7 . new Cesium.DebugCameraPrimitive (options)
相机可视化Primitive,常用于调试相机的相关参数,这个后面涉及到折射反射等功能。
6.Primitive的几何类型
Geomtry常用的一共有12中类型,每一种类型都有对应的边线模型的Geometry。