前面介紹了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支援以下幾何圖形:
image.png
image.png
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)。這些對象的關系如下圖所示:
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中。