Cesium(三) 几何图形与外观

                             几何图形与外观

我们可以通过Primitive API来操控几何图形及其外观,或者绘制各种特殊的形状。需要先得到Scene对象,然后在其上添加Primitive对象:

[javascript] view plain copy
  1. var viewer = new Cesium.Viewer('cesiumContainer');  
  2. var scene = viewer.scene;  
  3.    
  4. scene.primitives.add(new Cesium.RectanglePrimitive({  
  5.     //绘制矩形  
  6.     rectangle : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),  
  7.     material : Cesium.Material.fromType('Dot')  //设置材质  
  8. }));  

Primitive由两个部分组成:

  1. (1)几何形状(Geometry):定义了Primitive的结构,例如三角形、线条、点等
  2. (2)外观(Appearance ):定义Primitive的着色(Sharding),包括GLSL(OpenGL着色语言,OpenGL Shading Language)顶点着色器和片段着色器( vertex and fragment shaders),以及渲染状态(render state)

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 具有以下优势:

  1. (1)性能:绘制大量Primitive时,可以将其合并为单个Geometry以减轻CPU负担、更好的使用GPU。合并Primitive由web worker线程执行,UI保持响应性
  2. (2)灵活性:Geometry与Appearance 解耦,两者可以分别进行修改
  3. (3)低级别访问:易于编写GLSL 顶点、片段着色器、使用自定义的渲染状态 

同时,具有以下劣势:

  1. (1)需要编写更多地代码
  2. (2)需要对图形编程有更多的理解,特别是OpenGL的知识

使用来Geometry、Appearance 改写上面的例子,代码为:

[javascript] view plain copy
  1. var viewer = new Cesium.Viewer('cesiumContainer');  
  2. var scene = viewer.scene;  
  3. //GeometryInstance是Geometry的一个容器  
  4. var instance = new Cesium.GeometryInstance({  
  5.   geometry : new Cesium.RectangleGeometry({  
  6.     rectangle : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),  
  7.     vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT  
  8.   })  
  9. });  
  10. //使用抽象的Primitive而不是RectanglePrimitive  
  11. scene.primitives.add(new Cesium.Primitive({  
  12.   geometryInstances : instance,  
  13.   //使用该外观,可以使矩形覆盖在地球表面,或者悬浮一定的高度  
  14.   appearance : new Cesium.EllipsoidSurfaceAppearance({  
  15.     material : Cesium.Material.fromType('Dot')  
  16.   })  
  17. }));  
合并几何图形(Combing Geometries)

合并多个GeometryInstances 为一个Primitive可以极大的提高性能,下面的例子创建了2592一颜色各异的矩形,覆盖整个地球 :

[javascript] view plain copy
  1. var viewer = new Cesium.Viewer( 'cesiumContainer' );  
  2. var scene = viewer.scene;  
  3.    
  4. var instances = [];  
  5.    
  6. for ( var lon = -180.0; lon < 180.0; lon += 5.0 )  
  7. {  
  8.     for ( var lat = -90.0; lat < 90.0; lat += 5.0 )  
  9.     {  
  10.         instances.push( new Cesium.GeometryInstance( {  
  11.             geometry : new Cesium.RectangleGeometry( {  
  12.                 rectangle : Cesium.Rectangle.fromDegrees( lon, lat, lon + 5.0, lat + 5.0 )  
  13.             } ),  
  14.             attributes : {  
  15.                 color : Cesium.ColorGeometryInstanceAttribute.fromColor( Cesium.Color.fromRandom( {  
  16.                     alpha : 0.5  
  17.                 } ) )  
  18.             }  
  19.         } ) );  
  20.     }  
  21. }  
  22.    
  23. scene.primitives.add( new Cesium.Primitive( {  
  24.     geometryInstances : instances, //合并  
  25.     //某些外观允许每个几何图形实例分别指定某个属性,例如:  
  26.     appearance : new Cesium.PerInstanceColorAppearance()  
  27. } ) );  
选取几何图形(Picking)

即使多个 GeometryInstance被合并为单个Primitive,让然可以独立的被访问。我们可以为每一个GeometryInstance指定一个id,并且可以通过Scene.pick来判断该实例是否被选取:

[javascript] view plain copy
  1. var viewer = new Cesium.Viewer( 'cesiumContainer' );  
  2. var scene = viewer.scene;  
  3.    
  4. var instance = new Cesium.GeometryInstance( {  
  5.     geometry : new Cesium.RectangleGeometry( {  
  6.         rectangle : Cesium.Rectangle.fromDegrees( -100.0, 30.0, -90.0, 40.0 )  
  7.     } ),  
  8.     id : 'rectangle-1',  
  9.     attributes : {  
  10.         color : Cesium.ColorGeometryInstanceAttribute.fromColor( Cesium.Color.RED )  
  11.     }  
  12. } );  
  13.    
  14. scene.primitives.add( new Cesium.Primitive( {  
  15.     geometryInstances : instance,  
  16.     appearance : new Cesium.PerInstanceColorAppearance()  
  17. } ) );  
  18.    
  19. var handler = new Cesium.ScreenSpaceEventHandler( scene.canvas );  
  20. //设置单击事件的处理句柄  
  21. handler.setInputAction( function( movement )  
  22. {  
  23.     var pick = scene.pick( movement.position );  
  24.     if ( Cesium.defined( pick ) && ( pick.id === 'rectangle-1' ) )  
  25.     {  
  26.         console.log( '矩形被选取' );  
  27.     }  
  28. }, Cesium.ScreenSpaceEventType.LEFT_CLICK );  
几何图形实例(Geometry Instances)

上面的例子中,我们已经用到了GeometryInstances,注意GeometryInstance与Geometry的关系:前者是后者的容器,多个Instance可以共用一个Geometry,并且可以通过GeometryInstances.modelMatrix属性提供不同position、scale、rotate等位置、缩放、旋转信息。例如,下面的例子使用同一个Geometry绘制了两个Instance,一个位于另一个的上方:

[javascript] view plain copy
  1. var viewer = new Cesium.Viewer( 'cesiumContainer' );  
  2. var scene = viewer.scene;  
  3.    
  4. var ellipsoidGeometry = new Cesium.EllipsoidGeometry( {  
  5.     vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,  
  6.     radii : new Cesium.Cartesian3( 300000.0, 200000.0, 150000.0 )//三轴半径  
  7. } );  
  8. //下方的实例  
  9. var cyanEllipsoidInstance = new Cesium.GeometryInstance( {  
  10.     geometry : ellipsoidGeometry,  
  11.     modelMatrix : Cesium.Matrix4.multiplyByTranslation( Cesium.Transforms.eastNorthUpToFixedFrame( Cesium.Cartesian3.fromDegrees( -100.0, 40.0 ) ), new Cesium.Cartesian3( 0.0, 0.0, 150000.0 ) ),  
  12.     attributes : {  
  13.         color : Cesium.ColorGeometryInstanceAttribute.fromColor( Cesium.Color.CYAN )  
  14.     }  
  15. } );  
  16. //上方的实例  
  17. var orangeEllipsoidInstance = new Cesium.GeometryInstance( {  
  18.     geometry : ellipsoidGeometry,  
  19.     modelMatrix : Cesium.Matrix4.multiplyByTranslation( Cesium.Transforms.eastNorthUpToFixedFrame( Cesium.Cartesian3.fromDegrees( -100.0, 40.0 ) ), new Cesium.Cartesian3( 0.0, 0.0, 450000.0 ) ),  
  20.     attributes : {  
  21.         color : Cesium.ColorGeometryInstanceAttribute.fromColor( Cesium.Color.ORANGE )  
  22.     }  
  23. } );  
  24.    
  25. scene.primitives.add( new Cesium.Primitive( {  
  26.     geometryInstances : [  
  27.         cyanEllipsoidInstance, orangeEllipsoidInstance  
  28.     ],  
  29.     appearance : new Cesium.PerInstanceColorAppearance( {  
  30.         translucent : false,  
  31.         closed : true  
  32.     } )  
  33. } ) );  
更新单个GeometryInstance的属性

在添加到Primitive中以后,让然可以修改几何图形的某些属性:

  1. (1)颜色:如果Primitive设置了PerInstanceColorAppearance外观,则可以修改ColorGeometryInstanceAttribute类型的颜色
  2. (2)可见性:任何实例可以修改可见性

示例代码:

[javascript] view plain copy
  1. var viewer = new Cesium.Viewer( 'cesiumContainer' );  
  2. var scene = viewer.scene;  
  3.    
  4. var circleInstance = new Cesium.GeometryInstance( {  
  5.     geometry : new Cesium.CircleGeometry( {  
  6.         center : Cesium.Cartesian3.fromDegrees( -95.0, 43.0 ),  
  7.         radius : 250000.0,  
  8.         vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT  
  9.     } ),  
  10.     attributes : {  
  11.         color : Cesium.ColorGeometryInstanceAttribute.fromColor( new Cesium.Color( 1.0, 0.0, 0.0, 0.5 ) ),  
  12.         show : new Cesium.ShowGeometryInstanceAttribute( true ) //显示或者隐藏  
  13.     },  
  14.     id : 'circle'  
  15. } );  
  16. var primitive = new Cesium.Primitive( {  
  17.     geometryInstances : circleInstance,  
  18.     appearance : new Cesium.PerInstanceColorAppearance( {  
  19.         translucent : false,  
  20.         closed : true  
  21.     } )  
  22. } );  
  23. scene.primitives.add( primitive );  
  24.    
  25. //定期修改颜色  
  26. setInterval( function()  
  27. {  
  28.     var attributes = primitive.getGeometryInstanceAttributes( 'circle' );//获取某个实例的属性集  
  29.     attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue( Cesium.Color.fromRandom( {  
  30.         alpha : 1.0  
  31.     } ) );  
  32. }, 2000 );  
外观(Appearances)

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:

[javascript] view plain copy
  1. //下面的外观可用于定义一个Viewer不可进入的不透明盒子  
  2. var appearance = new Cesium.PerInstanceColorAppearance( {  
  3.     translucent : false,  
  4.     closed : true  
  5. } );  
  6. //下面的代码效果同上  
  7. var translucent = new Cesium.PerInstanceColorAppearance( {  
  8.     renderState : {  
  9.         depthTest : {  
  10.             enabled : true  
  11.         },  
  12.         cull : {  
  13.             enabled : true,  
  14.             face : Cesium.CullFace.BACK  
  15.         }  
  16.     }  
  17. } );  

一旦外观被创建,其render state就不可再变,但是其材质是可以替换的。另外Primitive的外观也是不可修改的。

大部分外观具有flat、faceForward属性,可以间接的控制GLSL 着色器:

  1. (1)flat:扁平化着色,不考虑光线的作用
  2. (2)faceForward:布尔值,控制光照效果
Geometry与Appearance的兼容性

需要注意,不是所有外观和所有几何图形可以搭配使用,例如EllipsoidSurfaceAppearance与WallGeometry就不能搭配,原因是后者是垂直于地表的。

即使外观与几何图形兼容,它们还必须有匹配的顶点格式(vertex formats)—— 即几何图形必须具有外观可以作为输入的数据格式,在创建Geometry时可以提供VertexFormat。

为了简便,可以让Geometry计算所有顶点属性(vertex attributes),以使之适用于任何外观,但这样做效率较差:

[javascript] view plain copy
  1. var geometry = new Cesium.RectangleGeometry( {  
  2.     vertexFormat : Cesium.VertexFormat.ALL  
  3. } );  
而如果我们使用外观EllipsoidSurfaceAppearance,其实只需要知道位置:

[javascript] view plain copy
  1. var geometry = new Ceisum.RectangleGeometry( {  
  2.     vertexFormat : Ceisum.VertexFormat.POSITION_ONLY  
  3. } );  
大部分外观具有vertexFormat属性或者VERTEX_FORMAT 静态常量,创建形状时只需要使用这些顶点格式即可:

[javascript] view plain copy
  1. var geometry = new Ceisum.RectangleGeometry( {  
  2.     vertexFormat : Ceisum.EllipsoidSurfaceAppearance.VERTEX_FORMAT  
  3. } );  
  4.    
  5. var geometry2 = new Ceisum.RectangleGeometry( {  
  6.     vertexFormat : Ceisum.PerInstanceColorAppearance.VERTEX_FORMAT  
  7. } );  
  8.    
  9. var appearance = new Ceisum.MaterialAppearance();  
  10. var geometry3 = new Ceisum.RectangleGeometry( {  
  11.     vertexFormat : appearance.vertexFormat  
  12. } );  

此外,两个形状必须具有匹配的vertexFormat,才能被合并到一个Primitive中。

发布了6 篇原创文章 · 获赞 31 · 访问量 20万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章