天天看點

cesium比較差別 opengl_Cesium中Primitive講解

前面介紹了entity方式繪制對象,現在我們開始接觸primitive方式,primitive方式更接近渲染引擎底層,由于我對webgl知之甚少,是以primitive接口我現在也是一知半解,寫這個部落格我參考了這一篇部落格Cesium(三) 幾何圖形與外觀。(https://blog.csdn.net/happyduoduo1/article/details/51868042)

1、Primitive由兩個部分組成

(1)幾何形狀(Geometry):定義了Primitive的結構,例如三角形、線條、點等

(2)外觀(Appearance ):定義Primitive的着色(Sharding),包括GLSL(OpenGL着色語言,OpenGL ShadingLanguage)頂點着色器和片段着色器( vertex and fragment shaders),以及渲染狀态(render state)

Cesium支援以下幾何圖形:

cesium比較差別 opengl_Cesium中Primitive講解

image.png

cesium比較差別 opengl_Cesium中Primitive講解

image.png

cesium比較差別 opengl_Cesium中Primitive講解

image.png

使用Geometry和Appearance 具有以下優勢:

(1)性能:繪制大量Primitive時,可以将其合并為單個Geometry以減輕CPU負擔、更好的使用GPU。合并Primitive由web worker線程執行,UI保持響應性

(2)靈活性:Geometry與Appearance 解耦,兩者可以分别進行修改

(3)低級别通路:易于編寫GLSL 頂點、片段着色器、使用自定義的渲染狀态

同時,具有以下劣勢:

(1)需要編寫更多地代碼

(2)需要對圖形程式設計有更多的了解,特别是OpenGL的知識

下面代碼是entity與primitive方式對比:

//entity方式

viewer.entities.add({

rectangle: {

coordinates: Cesium.Rectangle.fromDegrees(110.20, 34.55, 111.20, 35.55),

material: new Cesium.StripeMaterialProperty({

evenColor: Cesium.Color.WHITE,

oddColor: Cesium.Color.BLUE,

repeat:5

})

}

});

//primitive方式

var instance = new Cesium.GeometryInstance({

geometry: new Cesium.RectangleGeometry({

rectangle: Cesium.Rectangle.fromDegrees(105.20, 30.55, 106.20, 31.55),

vertexFormat:Cesium.EllipsoidSurfaceAppearance.VERTEXT_FORMAT

})

});

viewer.scene.primitives.add(new Cesium.Primitive({

geometryInstances: instance,

appearance: new Cesium.EllipsoidSurfaceAppearance({

material:Cesium.Material.fromType('Stripe')

})

}));

2、合并幾何圖形(Combing Geometries)

合并多個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()

} ) );

3、選取幾何圖形(Picking)

即使多個 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 );

4、幾何圖形執行個體(Geometry Instances)

上面的例子中,我們已經用到了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

} )

} ) );

5、更新單個GeometryInstance的屬性

在添加到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 );

6、外觀(Appearances)

Primitive由兩個重要部分組成:幾何圖形執行個體、外觀,一個Primitive隻能有一個外觀,而可以有多個執行個體。幾何圖形定義了結構,外觀定義了每個像素被如何着色,外觀可能使用材質(Material)。這些對象的關系如下圖所示:

cesium比較差別 opengl_Cesium中Primitive講解

image.png

外觀定義了需要在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:布爾值,控制光照效果

7、Geometry與Appearance的相容性

需要注意,不是所有外觀和所有幾何圖形可以搭配使用,例如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

} );

此外,兩個形狀必須具有比對的vertexFormat,才能被合并到一個Primitive中。