Cesium原理篇:7最长的一帧之Entity(上)
?????? 之前的最長的一幀系列,我們主要集中在地形和影像服務(wù)方面。簡單說,之前我們都集中在地球是怎么造出來的,從這一系列開始,我們的目光從GLOBE上解放出來,看看球面上的地物是如何渲染的。本篇也是先開一個頭,講一下涉及到的類結(jié)構(gòu)和整體的流程,有一個系統(tǒng)的,概括的理解。
?????? 我們先看看Cesium的渲染隊列:
var Pass = {// 環(huán)境,比如大氣層,月亮,天空盒等ENVIRONMENT : 0,//之前介紹的ComputeEngine,比如影像服務(wù)里面的投影涉及的計算COMPUTE : 1,// 地球切片GLOBE : 2,// 貼地的GeometryGROUND : 3,// 不透明的GeometryOPAQUE : 4,// (半)透明的GeometryTRANSLUCENT : 5,OVERLAY : 6,NUMBER_OF_PASSES : 7 };?????? 如上是Cesium的渲染隊列,也是Cesium渲染的優(yōu)先級,非常的清晰。就像畫家作畫一樣,我們先渲染地球,這樣就有了一個落腳地(同時也有了深度紋理和模版緩沖區(qū)等),然后再畫貼地的地物,然后是那些不貼地的且不透明的地物,接著是哪些透明度的地物,這時之前渲染地物的顏色和深度緩沖區(qū),可以方便的進(jìn)行深度計算和顏色的疊加,最上面的則是Overlay,比如一些公告板。這樣一層層的渲染,建立起整個場景的層次感和立體感。
?????? Geometry的難點在于種類繁多,材質(zhì)類型也不少,技術(shù)上如何高效的渲染,根據(jù)是否貼地,是否透明等屬性來設(shè)定渲染的優(yōu)先級,根據(jù)材質(zhì)和Geometry的類型來打組和實例化。同時Geometry的Type也是多樣的,如何調(diào)度,并且將參數(shù)化的Geometry高效的分解為VBO三角面片,里面的水確實很深。萬事開頭難,我們先從簡單的說起。
var entities = viewer.entities;entities.add({rectangle : {coordinates : Cesium.Rectangle.fromDegrees(-92.0, 20.0, -86.0, 27.0),outline : true,outlineColor : Cesium.Color.WHITE,outlineWidth : 4,stRotation : Cesium.Math.toRadians(45),} });?????? 這是Cesium提供的添加Geometry的示例代碼。So easy,不是嗎。Viewer中提供了entities,是一個EntityCollection類,用戶創(chuàng)建的不同類型的Geometry,都可以通過add方法添加,其他的就交給Cesium,最終實現(xiàn)屏幕渲染。順藤摸瓜,我們先列舉出相關(guān)的類,以及它們之間關(guān)系草圖:
ViewerDataSourceDisplayDataSourceCollectionCustomDataSourceEntityCollectionEntity?????? 先不關(guān)注具體的細(xì)節(jié),不難理解,當(dāng)我們調(diào)用EntityCollection.add方法時,根據(jù)參數(shù)會new一個Entity對象,此時EntityCollection充當(dāng)了一個容器的作用。接著,Cesium內(nèi)部通過事件的機制,在DataSourceDisplay中根據(jù)Entity類型安排具體模塊,最終該模塊完成對應(yīng)Entity的解析工作。這里你就會發(fā)現(xiàn),在這個通訊過程中,EntityCollection和DataSourceDisplay是直接進(jìn)行消息傳遞的,直接繞開了CustomDataSource,那DataSource模塊又有什么鳥用,豈不是站著坑不干活。下面通過一個例子讓大家有一個形象了解。
?????? 首先,作為一名熱愛編碼的程序員而言,是否曾經(jīng)仰天長嘆,為什么那個誰誰誰天天不干活,工資還比我高,我不明白。想想自家公司那些占坑不干活的人都是什么角色——管理人員。CustomDataSource并沒有參與實際的工作中,當(dāng)狀態(tài)變化時,維護(hù)一下EntityCollection和DataSourceDisplay兩者的關(guān)聯(lián),保證他們之間消息的暢通。比如初始化時,CustomDataSource把EntityCollection帶到DataSourceDisplay處,拋下一句話,以后有什么事情就找他,多多聯(lián)系,然后立馬就閃人了。這時,一臉懵逼的EntityCollection說,我得趕緊接客戶去了,要不留個聯(lián)系方式,有什么事情我好通知你。DataSourceDisplay也是管理層,自然不肯交出手機號,立馬找到助理(Visualizer),拋下一句話“以后你倆直接聯(lián)系”。這樣,EntityCollection和Visualizer的通訊渠道就建立起來了。Visualizer我們后面會介紹,這里略過。
?????? DataSourceCollection的作用也大致如此,但不同于CustomDataSource主要是接散戶,消極怠慢。DataSourceCollection主要針對大客戶或集團(tuán)客戶,比如GeoJson,KML,CZML等,它就不敢怠慢了,根據(jù)每一種情況做好功課,熟悉每一個集團(tuán)的特點和特殊需要,提供專業(yè)高效的服務(wù)。通過上面的例子我們大概明白為什么要增加DataSource這一層,來方面管理,通過分層來優(yōu)化,消化異常情況,盡量保證EntityCollection和DataSourceDisplay之間的交流是標(biāo)準(zhǔn)化,才能保證它們的通暢。針對這一方面,在后面的章節(jié)中在詳細(xì)討論,我們還是把注意力放在在整個流程上。
?????? 如上主要是介紹了人事關(guān)系,現(xiàn)在我們再來看看業(yè)務(wù)流程。大家都有去銀行辦理業(yè)務(wù)的經(jīng)歷,這里DataSourceDisplay相當(dāng)于這個銀行大廳的大廳經(jīng)理,主要維持好秩序,而真正工作的則是大廳經(jīng)理的手下——營業(yè)員,也就是我們剛剛提到的Visualizer。每天早上開工前(初始化階段),大廳經(jīng)理(DataSourceDisplay)把今天上班的營業(yè)員(Visualizer)都叫到一起,指著客戶經(jīng)理(EntityCollection)說,這位就是客戶經(jīng)理,所有的客戶都是他來負(fù)責(zé),你們認(rèn)識一下,一定要和客戶經(jīng)理好好配合,服務(wù)好每一位客戶啊。代碼如下,這里又出來了一個Updater模塊,實際上內(nèi)部是Updater處理每一個客戶的具體業(yè)務(wù)(將Entity轉(zhuǎn)化為最終的Primitive),我們不妨認(rèn)為Updater就是每一個營業(yè)員手中的電腦吧:
DataSourceDisplay.defaultVisualizersCallback = function(scene, entityCluster, dataSource) {var entities = dataSource.entities;return [new BillboardVisualizer(entityCluster, entities),new GeometryVisualizer(BoxGeometryUpdater, scene, entities),new GeometryVisualizer(CylinderGeometryUpdater, scene, entities),new GeometryVisualizer(CorridorGeometryUpdater, scene, entities),new GeometryVisualizer(EllipseGeometryUpdater, scene, entities),new GeometryVisualizer(EllipsoidGeometryUpdater, scene, entities),new GeometryVisualizer(PolygonGeometryUpdater, scene, entities),new GeometryVisualizer(PolylineGeometryUpdater, scene, entities),new GeometryVisualizer(PolylineVolumeGeometryUpdater, scene, entities),new GeometryVisualizer(RectangleGeometryUpdater, scene, entities),new GeometryVisualizer(WallGeometryUpdater, scene, entities),new LabelVisualizer(entityCluster, entities),new ModelVisualizer(scene, entities),new PointVisualizer(entityCluster, entities),new PathVisualizer(scene, entities)]; };?????? 這里,客戶經(jīng)理(entities)在Visualizer的構(gòu)造函數(shù)階段耍了一個小花招,對每一個營業(yè)人員前面貼了一個標(biāo)簽,指明每一個營業(yè)員的業(yè)務(wù)范疇,跟每個營業(yè)員交代了一番,白紙黑字寫的清清楚楚,到時候我把他們的材料審一邊,然后讓他們直接找你了。做管理也是要有兩把刷子的,起碼要知人善用,人盡其才。這時他走到其中一個營業(yè)員GeometryVisualizer身邊仔細(xì)囑咐了具體工作,我們來看看GeometryVisualizer的構(gòu)造函數(shù),看看人家的套路:
function GeometryVisualizer(type, scene, entityCollection) {// Key 1this._type = type;// Key 2for (var i = 0; i < numberOfShadowModes; ++i) {this._outlineBatches[i] = new StaticOutlineGeometryBatch(primitives, scene, i);this._closedColorBatches[i] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, true, i);this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, true, i);this._openColorBatches[i] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, false, i);this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, false, i);}// Key 3entityCollection.collectionChanged.addEventListener(GeometryVisualizer.prototype._onCollectionChanged, this) }????? 假設(shè)給他的安排(參數(shù))如下:new GeometryVisualizer(RectangleGeometryUpdater, scene, entities)。 我們省去多余的客套話,直接看里面的三個要點:
- Key 1
你今天就專門負(fù)責(zé)Rectangle身份的客戶 - Key 2
Rectangle屬于幾何對象,這類幾何身份的客戶,可能會有輪廓線,你得把他們的輪廓線信息放到_outlineBatches這一欄,它們有可能是閉合的,或者非閉合狀態(tài),也需要分別把他們的顏色和材質(zhì)都篩選到對應(yīng)欄 - Key 3
好了,我就簡單的說道這里,一會Rectangle類的客戶來了,我讓它直接找你。淺層意思就是沒事別找我,有事自己搞
?????? 細(xì)數(shù)下來,一共有六類客戶,分別是:
- BillboardVisualizer
- GeometryVisualizer
- LabelVisualizer
- ModelVisualizer
- PointVisualizer
- PathVisualizer
?????? 其中,GeometryVisualizer是最常見的,就是我們最常見的屌絲,因為屌絲眾多,屌絲和屌絲之間也是各有千秋,我們根據(jù)它們的外形(Geometry)分成了十類:
- BoxGeometry
- CylinderGeometry
- CorridorGeometry
- EllipseGeometry
- EllipsoidGeometry
- PolygonGeometry
- PolylineGeometry
- PolylineVolumeGeometry
- RectangleGeometry
- WallGeometryUpdater
?????? EntityCollection分別就這六大類詳細(xì)說明,有針對Geometry這類的十種情況額外強調(diào)一番。就到了營業(yè)時間。各就各位,準(zhǔn)備接客。
EntityCollection.prototype.add
?????? 我們再次回到本文開頭的代碼片段:
entities.add({rectangle : {coordinates : Cesium.Rectangle.fromDegrees(-92.0, 20.0, -86.0, 27.0),outline : true,outlineColor : Cesium.Color.WHITE,outlineWidth : 4,stRotation : Cesium.Math.toRadians(45),} });?????? 經(jīng)常上面的介紹,我們比較清楚了DataSourceDisplay->DataSource->EntityCollection->Entity之間的層級關(guān)聯(lián),自然,專注的重點轉(zhuǎn)到了業(yè)務(wù)流程。該客戶類型為rectangle,有位置,有外邊框,且寬度為4,顏色是白色,沿中心旋轉(zhuǎn)45度。
EntityCollection.prototype.add = function(entity) {if (!(entity instanceof Entity)) {entity = new Entity(entity);}var id = entity.id;if (!this._removedEntities.remove(id)) {this._addedEntities.set(id, entity);}fireChangedEvent(this);return entity; };?????? EntityCollection看到這位客戶,熟練的做了三個事情:1把它手里的材料整理好;2給它一張排隊的小票;3通知對應(yīng)的Visualizer接客。我們把這三個過程一一道來。
1.new Entity
function Entity(options) {// 獲取一個唯一id// 可以指定,也可以系統(tǒng)自動創(chuàng)建一個var id = options.id;if (!defined(id)) {id = createGuid();}// 解析參數(shù),把對應(yīng)的屬性賦給該entitythis.merge(options); }Entity.prototype.merge = function(source) { // 獲取屬性名,根據(jù)代碼輸入的參數(shù),可知// 調(diào)用的是Object.keys(source)// 返回值是一個數(shù)組:0: "rectangle"var sourcePropertyNames = defined(source._propertyNames) ? source._propertyNames : Object.keys(source);var propertyNamesLength = sourcePropertyNames.length;for (var i = 0; i < propertyNamesLength; i++) {var name = sourcePropertyNames[i];// 獲取參數(shù)中"rectangle"對應(yīng)的屬性var sourceProperty = source[name];if (defined(sourceProperty)) {// 將sourceProperty賦予該entity對應(yīng)的rectangle屬性this[name] = sourceProperty;}} };?????? 這段代碼看上去平淡無奇,很自然的通過用戶輸入的Object封裝成一個新的Entity。實際上,這得益于Cesium強大的屬性封裝。在Cesium中,簡單的屬性可以通過defineProperties的形式,內(nèi)部是采用Object.defineProperties的方式來提供set和get方法,但Entity中,不同的Geometry對應(yīng)的屬性并不完全相同,比如矩形是有左上角和右下角兩個點就可以明確其position屬性,而圓則需要圓心+半徑,風(fēng)格上也有一定差異;同時還有很多相同的屬性,比如顏色等;另外一個特點是Entity對應(yīng)的類型是可枚舉的,所以Cesium在提供了createPropertyDescriptor類進(jìn)行了封裝,提高重用度。
defineProperties(Entity.prototype, {rectangle : createPropertyTypeDescriptor('rectangle', RectangleGraphics) };?????? 如上通過createPropertyTypeDescriptor實現(xiàn)了Entity中rectangle屬性,正因為如此,才保證我們能夠做到this[“rectangle”] = sourceProperty。另外,這里出現(xiàn)了一個新類RectangleGraphics,實際上是它針對rectangle的json參數(shù)進(jìn)行的封裝,此時Entity對應(yīng)的rectangle屬性,已經(jīng)有初始化時的createPropertyDescriptor返回的function變成了該RectangleGraphics。此時的RectangleGraphics內(nèi)部保存的是參數(shù)化的數(shù)據(jù)內(nèi)容,并不參與幾何數(shù)據(jù)處理的過程。稍后會提到。
?
2.EntityCollection._addedEntities
?????? 當(dāng)Entity創(chuàng)建成功后,就好比客戶經(jīng)理檢查完你的材料,確認(rèn)無誤后,給你一張小條,上面寫著你的id,然后就是排隊了:
EntityCollection.prototype.add = function(entity) {if (!this._removedEntities.remove(id)) { this._addedEntities.set(id, entity);}return entity; };?????? 這里想說的是,EntityCollection總共有三個更新隊列:_addedEntities,_removedEntities,_changedEntities。由此可以得出,Entity的調(diào)度和Globe在設(shè)計上也是如出一轍,都是基于狀態(tài)的。
3.fireChangedEvent
?????? 當(dāng)把該Entity放入添加隊列后,EntityCollection則通過ChangedEvent事件,通知所有綁定該事件的Visualizer,有新的Entity進(jìn)來了。還記得Visualizer初始化時綁定的_onCollectionChanged事件:
GeometryVisualizer.prototype._onCollectionChanged = function(entityCollection, added, removed) {var addedObjects = this._addedObjects;var removedObjects = this._removedObjects;var changedObjects = this._changedObjects;var i;var id;var entity;for (i = added.length - 1; i > -1; i--) {entity = added[i];id = entity.id;if (removedObjects.remove(id)) {changedObjects.set(id, entity);} else {addedObjects.set(id, entity);}} };?????? 針對EntityCollection的每一種隊列,Visualizer分別提供了_addedObjects,_removedObjects,_changedObjects三個隊列一一對接,負(fù)責(zé)的營業(yè)員一個不落的把客戶從客戶經(jīng)理手中轉(zhuǎn)交到自己的柜臺前。這里,個人覺得Cesium還是有一個優(yōu)化的空間,當(dāng)然價值大不大就不清楚了。因為每一個Visualizer都會收到消息,說有一個新來的Entity,需要接待一下。這時,大家并不知道該Entity的類型,所以無法判斷具體應(yīng)該是哪一個Visualizer去接待。所以大家每個人都把該entity加到自己的添加隊列了,這個在如上的代碼可以看到。,最終是每一個Visualizer對應(yīng)的Updater來解析該Entity,看看是不是自己的菜。其實我覺得,_onCollectionChanged事件中,可以很簡單的通過Type來判斷是否是當(dāng)前Updater是否可解析該Entity,就好比看看菜單就能知道是否是自己的菜,沒必要非要自己吃一口。
?????? 如上,我們在類的層次關(guān)系以及事件驅(qū)動兩個維度介紹了AddEntity涉及到的具體內(nèi)容。下篇介紹如何處理這些數(shù)據(jù),最終通過DrawCommand使其可渲染。
轉(zhuǎn)載于:https://www.cnblogs.com/fuckgiser/p/6091040.html
總結(jié)
以上是生活随笔為你收集整理的Cesium原理篇:7最长的一帧之Entity(上)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hsdb
- 下一篇: python-类知识点简介