一、primitive简介
1、概念:Primitive 是Cesium中用于绘制几何图形的另一个重要的接口,相对于Entity来说,它更接近渲染引擎底层,主要面向图形开发人员,意味着使用Primitive来绘制几何图形需要您具备一定的图形开发知识。
2、引用示例
在场景中添加 Entity 是通过viewer.entities.add方法,而添加Primitive则是通过viewer.scene.primitives.add方法。下面创建一个圆几何,通过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
}
}
})
})
}));
3、总结
一个Primitive实例必须设置geometryInstances和appearance两个属性,才能正确地在场景中显示出来。geometryInstances代表要显示的几何形状,而appearance则代码该几何的外观样式。
为什么要有两套绘制图形的接口:
1、Entity是Cesium封装的比较高级的绘图接口,主要面向普通的开发人员,即使您没有任何图形开发知识,您也能使用Entity在场景中快速绘制各种几何形状。
2、Primitive是Cesium封装的更低级绘图接口,绘图方式更接近渲染引擎底层,但又不至于直接使用WebGL底层的绘图接口,其主要面向图形开发人员。
3、使用Primitive的第一大优势是提供了GeometryInstance 即几何实例化的技术,在渲染大量静态数据时很有用,能够减少CPU开销,充分利用GPU的性能。
4、使用Primitive的第二大优势是提供了顶点着色器和片元着色器的编程接口,可以充分发挥您的图形开发技术。
二、primitive的构成
1、Primitive 主要由以下两部分构成:Geometry 几何形状,用于定义Primitive显示的几何形状;Appearance 外观,用于决定Primitive的着色或渲染。
2、通俗来说Geometry就是骨架,而Appearance就是皮肤。一个 Primitive 可以包含多个Geometry,但是只能有一个Appearance,比如下面的代码通过一个Primitive创建10个Geometry:
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')
})
}));
3、每个Geometry是通过GeometryInstance来进行实例化的,通过GeometryInstance对Geometry进行实例化,通过GeometryInstance对Geometry进行实例化,同一个Geometry可以被实例化多次。这对于展示大量数据时很有用,比如有100万个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 < 1000000; 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')
})
}));
4、我们还可以通过对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 < 1000; 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);
5、需要注意的是,因为一个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 < 100; 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)),
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
})
}));
三、primitive的优缺点
1、优点:性能好,使用Primitive可以将多个Geometry合并为一个大的Geometry减少CPU的开销,更充分利用GPU。合并过程使用WebWorker不影响UI响应。灵活度高,Primitive由Geometry和Appearance构成,我们可以单独修改它们。可编程性强,可以操作顶点着色器和片元着色器,还可以自定义渲染。
2、缺点:使用更为复杂,需要编写更多的代码,需要更多的图形学知识。组合图形对于动态数据不是很友好,比如使用Primitive组合多个Geometry时无法更新Geometry。
3、综合对比:通过Entity和Primitve分别创建100万个Box进行性能测试,帧率相差很大。海量数据的应用必选primitive
Entity创建Box:
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(110.0, 30.0, 500),
box: {
dimensions: new Cesium.Cartesian3(500.0, 500.0, 500.0),
material:Cesium.Color.BLUE
}
})
Primitive创建Box:
const box = Cesium.BoxGeometry.fromDimensions({
dimensions: new Cesium.Cartesian3(500.0, 500.0, 500.0)
});
const geometry = Cesium.BoxGeometry.createGeometry(box);
const instance = new Cesium.GeometryInstance({
geometry: geometry,
modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(110.0, 30.0, 500)),
});
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的分类
Cesium中除了Primitive类以外,还有一些以Primitive结尾的类,这些类的使用方式和Primitive类似,可以将其视为Primitive的同类。
1、GroundPrimitive:贴地Primitive,将几何图形贴地显示,适用于平面类型的Geometry, 比如CircleGeometry、PolygonGeometry、RectangleGeometry。贴地Primitive即是将图形从上往下贴,可以通过classificationType属性设置贴的目标类型。
2、ClassificationPrimitive:分类Primitive,将几何体贴模型高亮显示,适用于体类型的Geometry 比如BoxGeometry、 CylinderGeometry,也适用于设置拉伸后的平面类型的Geometry。分类Primitive常用于高亮显示模型,比如倾斜模型的分层单体化或者分户单体化就可以使用该类来实现。
3、GroundPolylinePrimitive:贴地线,仅用于GroundPolylineGeometry
4、PointPrimitive:点,不直接new 而是通过下方的PointPrimitiveCollection .add方法 添加
5、PointPrimitiveCollection:点集合
6、VoxelPrimitive
7、DebugCameraPrimitive:相机可视化,常用于调试一个相机的相关参数
五、Primitive的几何类型
官方分类,总共有12种,并且每一种都有对应的边线模式的Geometry类型
1、BoxGeometry 盒子几何,比如长方体、正方体创建一个盒子,我们通常是通BoxGeometry.fromDimensions()方法,而不是new BoxGeometry(),因为fromDimensions可以直接设置盒子的长宽高。BoxGeometry没有提供设置位置的属性,所以我们需要通过GeometryInstance的modelMatrix属性将其定位到地球上正确的位置。
const box = Cesium.BoxGeometry.fromDimensions({
dimensions: new Cesium.Cartesian3(