我们可以通过Primitive API来操控几何图形及其外观,或者绘制各种特殊的形状。需要先得到Scene对象,然后在其上添加Primitive对象:
var viewer = new Cesium.Viewer('cesiumContainer'); var scene = viewer.scene; scene.primitives.add(new Cesium.RectanglePrimitive({ //绘制矩形 rectangle : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0), material : Cesium.Material.fromType('Dot') //设置材质 }));
Primitive由两个部分组成:
Cesium支持以下几何图形:
几何图形 | 说明 |
BoxGeometry | 立方体 |
BoxOutlineGeometry | 仅有轮廓的立方体 |
CircleGeometry | 圆形或者拉伸的圆形 |
CircleOutlineGeometry | 只有轮廓的圆形 |
CorridorGeometry | 走廊:沿着地表的多段线,且具有一定的宽度,可以拉伸到一定的高度 |
CorridorOutlineGeometry | 只有轮廓的走廊 |
CylinderGeometry | 圆柱、圆锥或者截断的圆锥 |
CylinderOutlineGeometry | 只有轮廓的圆柱、圆锥或者截断的圆锥 |
EllipseGeometry | 椭圆或者拉伸的椭圆 |
EllipseOutlineGeometry | 只有轮廓的椭圆或者拉伸的椭圆 |
EllipsoidGeometry | 椭球体 |
EllipsoidOutlineGeometry | 只有轮廓的椭球体 |
RectangleGeometry | 矩形或者拉伸的矩形 |
RectangleOutlineGeometry | 只有轮廓的矩形或者拉伸的矩形 |
PolygonGeometry | 多边形,可以具有空洞或者拉伸一定的高度 |
PolygonOutlineGeometry | 只有轮廓的多边形 |
PolylineGeometry | 多段线,可以具有一定的宽度 |
SimplePolylineGeometry | 简单的多段线 |
PolylineVolumeGeometry | 多段线柱体 |
PolylineVolumeOutlineGeometry | 只有轮廓的多段线柱体 |
SphereGeometry | 球体 |
SphereOutlineGeometry | 只有轮廓的球体 |
WallGeometry | 墙 |
WallOutlineGeometry | 只有轮廓的墙 |
使用Geometry和Appearance 具有以下优势:
同时,具有以下劣势:
使用来Geometry、Appearance 改写上面的例子,代码为:
var viewer = new Cesium.Viewer('cesiumContainer'); var scene = viewer.scene; //GeometryInstance是Geometry的一个容器 var instance = new Cesium.GeometryInstance({ geometry : new Cesium.RectangleGeometry({ rectangle : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0), vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT }) }); //使用抽象的Primitive而不是RectanglePrimitive scene.primitives.add(new Cesium.Primitive({ geometryInstances : instance, //使用该外观,可以使矩形覆盖在地球表面,或者悬浮一定的高度 appearance : new Cesium.EllipsoidSurfaceAppearance({ material : Cesium.Material.fromType('Dot') }) }));
合并多个GeometryInstances 为一个Primitive可以极大的提高性能,下面的例子创建了2592一颜色各异的矩形,覆盖整个地球 :
var viewer = new Cesium.Viewer( 'cesiumContainer' ); var scene = viewer.scene; var instances = []; for ( var lon = -180.0; lon < 180.0; lon += 5.0 ) { for ( var lat = -90.0; lat < 90.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 ) } ), attributes : { color : Cesium.ColorGeometryInstanceAttribute.fromColor( Cesium.Color.fromRandom( { alpha : 0.5 } ) ) } } ) ); } } scene.primitives.add( new Cesium.Primitive( { geometryInstances : instances, //合并 //某些外观允许每个几何图形实例分别指定某个属性,例如: appearance : new Cesium.PerInstanceColorAppearance() } ) );
即使多个 GeometryInstance被合并为单个Primitive,让然可以独立的被访问。我们可以为每一个GeometryInstance指定一个id,并且可以通过Scene.pick来判断该实例是否被选取:
var viewer = new Cesium.Viewer( 'cesiumContainer' ); var scene = viewer.scene; var instance = new Cesium.GeometryInstance( { geometry : new Cesium.RectangleGeometry( { rectangle : Cesium.Rectangle.fromDegrees( -100.0, 30.0, -90.0, 40.0 ) } ), id : 'rectangle-1', attributes : { color : Cesium.ColorGeometryInstanceAttribute.fromColor( Cesium.Color.RED ) } } ); scene.primitives.add( new Cesium.Primitive( { geometryInstances : instance, appearance : new Cesium.PerInstanceColorAppearance() } ) ); var handler = new Cesium.ScreenSpaceEventHandler( scene.canvas ); //设置单击事件的处理句柄 handler.setInputAction( function( movement ) { var pick = scene.pick( movement.position ); if ( Cesium.defined( pick ) && ( pick.id === 'rectangle-1' ) ) { console.log( '矩形被选取' ); } }, Cesium.ScreenSpaceEventType.LEFT_CLICK );
上面的例子中,我们已经用到了GeometryInstances,注意GeometryInstance与Geometry的关系:前者是后者的容器,多个Instance可以共用一个Geometry,并且可以通过GeometryInstances.modelMatrix属性提供不同position、scale、rotate等位置、缩放、旋转信息。例如,下面的例子使用同一个Geometry绘制了两个Instance,一个位于另一个的上方:
var viewer = new Cesium.Viewer( 'cesiumContainer' ); var scene = viewer.scene; var ellipsoidGeometry = new Cesium.EllipsoidGeometry( { vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT, radii : new Cesium.Cartesian3( 300000.0, 200000.0, 150000.0 )//三轴半径 } ); //下方的实例 var cyanEllipsoidInstance = new Cesium.GeometryInstance( { geometry : ellipsoidGeometry, modelMatrix : Cesium.Matrix4.multiplyByTranslation( Cesium.Transforms.eastNorthUpToFixedFrame( Cesium.Cartesian3.fromDegrees( -100.0, 40.0 ) ), new Cesium.Cartesian3( 0.0, 0.0, 150000.0 ) ), attributes : { color : Cesium.ColorGeometryInstanceAttribute.fromColor( Cesium.Color.CYAN ) } } ); //上方的实例 var orangeEllipsoidInstance = new Cesium.GeometryInstance( { geometry : ellipsoidGeometry, modelMatrix : Cesium.Matrix4.multiplyByTranslation( Cesium.Transforms.eastNorthUpToFixedFrame( Cesium.Cartesian3.fromDegrees( -100.0, 40.0 ) ), new Cesium.Cartesian3( 0.0, 0.0, 450000.0 ) ), attributes : { color : Cesium.ColorGeometryInstanceAttribute.fromColor( Cesium.Color.ORANGE ) } } ); scene.primitives.add( new Cesium.Primitive( { geometryInstances : [ cyanEllipsoidInstance, orangeEllipsoidInstance ], appearance : new Cesium.PerInstanceColorAppearance( { translucent : false, closed : true } ) } ) );
在添加到Primitive中以后,让然可以修改几何图形的某些属性:
示例代码:
var viewer = new Cesium.Viewer( 'cesiumContainer' ); var scene = viewer.scene; var circleInstance = new Cesium.GeometryInstance( { geometry : new Cesium.CircleGeometry( { center : Cesium.Cartesian3.fromDegrees( -95.0, 43.0 ), radius : 250000.0, vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT } ), attributes : { color : Cesium.ColorGeometryInstanceAttribute.fromColor( new Cesium.Color( 1.0, 0.0, 0.0, 0.5 ) ), show : new Cesium.ShowGeometryInstanceAttribute( true ) //显示或者隐藏 }, id : 'circle' } ); var primitive = new Cesium.Primitive( { geometryInstances : circleInstance, appearance : new Cesium.PerInstanceColorAppearance( { translucent : false, closed : true } ) } ); scene.primitives.add( primitive ); //定期修改颜色 setInterval( function() { var attributes = primitive.getGeometryInstanceAttributes( 'circle' );//获取某个实例的属性集 attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue( Cesium.Color.fromRandom( { alpha : 1.0 } ) ); }, 2000 );
Primitive由两个重要部分组成:几何图形实例、外观,一个Primitive只能有一个外观,而可以有多个实例。几何图形定义了结构,外观定义了每个像素被如何着色,外观可能使用材质(Material)。这些对象的关系如下图所示:
Cesium支持下表列出的外观:
外观 | 说明 |
MaterialAppearance | 支持各种Geometry类型的外观,支持使用材质来定义着色 |
EllipsoidSurfaceAppearance | MaterialAppearance的一个版本。假设几何图形与地表是平行的,并且依此来进行顶点属性(vertex attributes)的计算 |
PerInstanceColorAppearance | 让每个实例使用自定义的颜色来着色 |
PolylineMaterialAppearance | 支持使用材质来着色多段线 |
PolylineColorAppearance | 使用每顶点或者每片段(per-vertex or per-segment )的颜色来着色多段线 |
外观定义了需要在GPU上执行的完整的GLSL顶点、片段着色器,通常不需要修改这一部分,除非需要定义自己的外观。
外观还定义了完整的render state,用于在绘制Primitive时控制GPU的状态,可以直接或者通过高层API来定义render state:
//下面的外观可用于定义一个Viewer不可进入的不透明盒子 var appearance = new Cesium.PerInstanceColorAppearance( { translucent : false, closed : true } ); //下面的代码效果同上 var translucent = new Cesium.PerInstanceColorAppearance( { renderState : { depthTest : { enabled : true }, cull : { enabled : true, face : Cesium.CullFace.BACK } } } );
一旦外观被创建,其render state就不可再变,但是其材质是可以替换的。另外Primitive的外观也是不可修改的。
大部分外观具有flat、faceForward属性,可以间接的控制GLSL 着色器:
需要注意,不是所有外观和所有几何图形可以搭配使用,例如EllipsoidSurfaceAppearance与WallGeometry就不能搭配,原因是后者是垂直于地表的。
即使外观与几何图形兼容,它们还必须有匹配的顶点格式(vertex formats)—— 即几何图形必须具有外观可以作为输入的数据格式,在创建Geometry时可以提供VertexFormat。
为了简便,可以让Geometry计算所有顶点属性(vertex attributes),以使之适用于任何外观,但这样做效率较差:
var geometry = new Cesium.RectangleGeometry( { vertexFormat : Cesium.VertexFormat.ALL } );而如果我们使用外观EllipsoidSurfaceAppearance,其实只需要知道位置:
var geometry = new Ceisum.RectangleGeometry( { vertexFormat : Ceisum.VertexFormat.POSITION_ONLY } );大部分外观具有vertexFormat属性或者VERTEX_FORMAT 静态常量,创建形状时只需要使用这些顶点格式即可:
var geometry = new Ceisum.RectangleGeometry( { vertexFormat : Ceisum.EllipsoidSurfaceAppearance.VERTEX_FORMAT } ); var geometry2 = new Ceisum.RectangleGeometry( { vertexFormat : Ceisum.PerInstanceColorAppearance.VERTEX_FORMAT } ); var appearance = new Ceisum.MaterialAppearance(); var geometry3 = new Ceisum.RectangleGeometry( { vertexFormat : appearance.vertexFormat } );