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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章