Cesium原理篇:1最長的一幀之渲染調度


       原計划開始着手地形系列,但發現如果想要從邏輯上徹底了解地形相關的細節,那還是需要了解Cesium的數據調度過程,這樣才能更好的理解,因此,打算先整體介紹一下Cesium的渲染過程,然后在過渡到其中的兩個主要模塊:地形數據和影像數據。

簡述

       設想一下,印度洋的暖流,穿過喜馬拉雅山,形成了滴一滴水,落在了青藏高原的唐古拉山,順勢而下,涌入太平洋,長江之水自此經久不息。而Cesium的一切的一切,也是從一個並不起眼的函數開始的:

 

clip_image001

 

       通過requestAnimationFrame函數,每一幀的結束,就是下一幀的開始,從Cesium啟動之時,直至其消亡,不曾停歇。

       現在,我們就開始萬里長征第一步,最終,該函數會調用Scene的Render方法:

 

clip_image002

 

       雖然render(scene, time)的方法很長,但對於地球的主要要素(地形&影像),都是在Globe的beginFrame和endFrame之間完成的:

 

clip_image004

 

       其中紅色高亮的兩個函數用來維護兩個重要的邏輯,updateAndExecuteCommands負責數據的調度,比如哪些Tile需要創建,這些Tile相關的地形數據,以及涉及到的影像數據之間的調度,都是在該函數中維護。但是updateAndExecuteCommands函數只負責管理,也就是他所創建的Tile類只是一個個的Tasks,自己並不負責Tasks內容的實現(數據的加載和維護)。這樣,導致架構會有些復雜,因為不是一個流水線的作業方式,所以就需要有狀態的維護。但這也是必然的,不僅保證該函數高效,不拖累每一幀的時間消耗,同時因為實時性滿足人的要求(幀數能夠達到或接近60),每一幀需要的Tile隊列可以實時計算,不用考慮時間和狀態之間的關系。

       可以這樣理解,updateAndExecuteCommands是一個管理者,只說不做,與之相反,scene.globe.endFrame中,會對該幀所涉及的GlobeTile的下載,解析等進行處理。

 

clip_image006

 

       如上圖,簡單而言,updateAndExecuteCommands負責上面看得見的事情,根據當前這一幀相機的狀態,先打一個黃色的網格,然后檢查當前狀態下,這些網格里面的地形和影像數據是否都已經准備好了,如果准備好,每一個Tile就創建一個DrawCommand來渲染,把這個球畫出來。如果沒准備好也不管,反正有endFrame函數負責,各司其職,每個人做好本職工作,不是很好嗎。

       scene.globe.endFrame就按照吩咐來准備每一個Tile的數據,全是看不見的工作,默默的奉獻自己的青春。這里,Promise,Worker等技術都會使用,通過異步和多線程的方式,來緩解每一幀的負擔,而在主線程下,主要是數據狀態的維護。如果一個Tile的數據解析完成,則該GlobeTile的狀態更新為renderable(可渲染),done(已完成)。

類關系

       在這個過程中,類的關系大致如下:

 

clip_image007

       網格按照經緯度來划分的,每一個網格對應一個QuadtreeTile四叉樹類,其中有一個GlobeSurfaceTile,里面保存地形數據TileTerrain和影像數據TileImagery。當前相機下需要多個網格,就需要多個QuadtreeTile,該隊列保存在QuadtreePrimitive,QuadtreePrimitive統一維護所有Tile的管理,具體是通過GlobeSurfaceTileProvider來實現邏輯操作,而QuadtreePrimitive上面包了一個Globe和Scene。

渲染過程

       updateAndExecuteCommands的關系過程如下:

 

clip_image009

 

       在QuadtreePrimitive的update中,調用selectTilesForRendering來或許當前需要創建的網格,其中,在第一幀會把全球分為兩塊Tile:

 

clip_image011

 

       而在queueChildrenLoadAndDetermineIfChildrenAreAllRenderable函數中,基於這兩個Tile來做四叉樹剖分,也就是所有的后來的QuadtreeTile都能夠上溯到這兩個Tile之一:

 

clip_image013

 

       創建的GlobeSurfaceTile都會存儲到更新隊列中,等待數據下載和解析的過程,而如果當前幀中,存在准備好的,已經可以渲染的GlobeSurfaceTile,則調用addDrawCommandsForTile,構造成VBO,最終通過WebGL實現渲染。

更新過程

       更新隊列的邏輯如上,稍顯復雜,還是着重看框選的部分:

 

clip_image015

       最初,在processTileLoadQueue中,遍歷更新隊列中所有的GlobeSurfaceTile,如果發現里面沒有數據,狀態是QuadtreeTileLoadState. START,於是乎,大聲喊道:“姐妹們,開始接客了”。

       青樓就在GlobeSurfaceTile.processStateMachine。GlobeSurfaceTile就是這里的顧客,盡管人潮涌動,但來此處的人,不外乎三種:第一次來的,您就到prepareNewTile函數來吧,先幫你疏通疏通筋骨:new TileTerrain(),新建地形數據類,在幫你潤潤肌膚:_createTileImagerySkeletons,創建影像數據類;原來是老司機了,那就老規矩,來個三溫暖吧:processTerrainStateMachine來負責地形數據加載,createWaterMaskTextureIfNeeded負責水面(如果該Tile是海洋區域的話),還有TileImagery.prototype.processStateMachine負責影像數據的加載;服務滿意嗎,來結賬買單了:renderable = true/state = QuadtreeTileLoadState.DONE。

       通過上面的描述,一個地球的渲染過程主要四個環節:

l 網格划分

l Terrain數據

l Imagery數據

l 渲染

       而這四個環節互相交錯,雖然看上去有些混亂,函數眾多,但不知道你是否注意到,無論是渲染過程還是更新過程,都是針對當前狀態的響應和狀態的更新。

       從流程上講了一下大致的過程,不知道大家是否還有疑惑,或者文中有不對的地方,也希望多提意見。當然這個顆粒度還有點大,后面,在和大家分享一下每一個環節中具體的技術和優化策略。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM