幾何圖形與外觀
我們可以通過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由兩個部分組成:
- (1)幾何形狀(Geometry):定義了Primitive的結構,例如三角形、線條、點等
- (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)性能:繪製大量Primitive時,可以將其合併爲單個Geometry以減輕CPU負擔、更好的使用GPU。合併Primitive由web worker線程執行,UI保持響應性
- (2)靈活性:Geometry與Appearance 解耦,兩者可以分別進行修改
- (3)低級別訪問:易於編寫GLSL 頂點、片段着色器、使用自定義的渲染狀態
同時,具有以下劣勢:
- (1)需要編寫更多地代碼
- (2)需要對圖形編程有更多的理解,特別是OpenGL的知識
使用來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中以後,讓然可以修改幾何圖形的某些屬性:
- (1)顏色:如果Primitive設置了PerInstanceColorAppearance外觀,則可以修改ColorGeometryInstanceAttribute類型的顏色
- (2)可見性:任何實例可以修改可見性
示例代碼:
- 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 着色器:
- (1)flat:扁平化着色,不考慮光線的作用
- (2)faceForward:布爾值,控制光照效果
需要注意,不是所有外觀和所有幾何圖形可以搭配使用,例如EllipsoidSurfaceAppearance與WallGeometry就不能搭配,原因是後者是垂直於地表的。
即使外觀與幾何圖形兼容,它們還必須有匹配的頂點格式(vertex formats)—— 即幾何圖形必須具有外觀可以作爲輸入的數據格式,在創建Geometry時可以提供VertexFormat。
爲了簡便,可以讓Geometry計算所有頂點屬性(vertex attributes),以使之適用於任何外觀,但這樣做效率較差:
- var geometry = new Cesium.RectangleGeometry( {
- vertexFormat : Cesium.VertexFormat.ALL
- } );
- var geometry = new Ceisum.RectangleGeometry( {
- vertexFormat : Ceisum.VertexFormat.POSITION_ONLY
- } );
- 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
- } );
此外,兩個形狀必須具有匹配的vertexFormat,才能被合併到一個Primitive中。