Bootstrap

Cesium进阶学习一、Primitive

一、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(
;