? ? ? Cesium是一個(gè)開源的缸濒,基于webgl的二、三維地圖引擎植榕,就其實(shí)現(xiàn)來說是目前開源版本中比較完備的一個(gè)版本再沧,有完備的數(shù)據(jù)源支持、支持大場景尊残、支持定制化的樣式渲染炒瘸。由于發(fā)起人是個(gè)三維可視化方面的大牛,我很樂意花點(diǎn)時(shí)間去學(xué)習(xí)一下大牛的作品寝衫,一探究竟顷扩,Cesium是如何實(shí)現(xiàn)大型(超過150000行代碼)webgl前端程序的組織和渲染調(diào)度的。
? ? ? Cesium就其支持的數(shù)據(jù)格式而言慰毅,支持大部分影像瓦片格式(當(dāng)然是世界范圍的隘截,國內(nèi)的幾個(gè)廠商自定的格式,目前官方還沒有完全支持汹胃,但是可以通過自定義瓦片格式去訪問瓦片也不是什么大問題)婶芭,Json格式的矢量數(shù)據(jù),大場景數(shù)據(jù)格式3D Tiles着饥,小場景的可以直接用gltf格式雕擂。影像和矢量目前行業(yè)內(nèi)的方案比較成熟了,Cesium也只是兼收并蓄贱勃,沒有特別出彩的地方。我想說的是關(guān)于三維數(shù)據(jù)格式這一塊谤逼,這個(gè)gltf是由Khronos Group設(shè)計(jì)和規(guī)定的WebGL分發(fā)格式贵扰,對(duì)與前端渲染而言,緊湊的數(shù)據(jù)格式和高效的運(yùn)行時(shí)解析是保證效率的兩個(gè)關(guān)鍵因素流部,目前很多前端的庫(比如three.js)還試圖支持各種各樣的交換3d格式戚绕,比如3ds obj等這些,這些數(shù)據(jù)格式是PC桌面端時(shí)代的產(chǎn)物枝冀,其目的是為了便于人的理解而不是利于機(jī)器的理解舞丛,所以這些格式必然是無法適應(yīng)web場景的,而gltf將webgl中所需的幾何數(shù)據(jù)以二進(jìn)制的方式存儲(chǔ)果漾,當(dāng)然數(shù)據(jù)的組織是完全根據(jù)webgl接口的需要進(jìn)行組織球切,這樣一來即能保證格式的緊湊又能保證解析的高效,運(yùn)行時(shí)直接將數(shù)據(jù)塞給webgl而省卻了格式解析的過程绒障,基于這點(diǎn)考慮在三維模型上只支持gltf吨凑。在大場景數(shù)據(jù)格式方面,Cesium首先提出了3D Tiles的場景數(shù)據(jù)格式,雖然對(duì)于三維場景數(shù)據(jù)組織大家第一想法就是仿照二維瓦片的方式組織鸵钝,但是大家只是想了想糙臼,而人家是落地了,所以人家是先驅(qū)恩商,后期GIS屆的兩大廠商超圖和ESRI也陸續(xù)推出了自己的三維場景格式S3M和I3S变逃,基本原理都一樣,我就說個(gè)基本思路就是:N叉樹+三維瓦片怠堪。而每個(gè)瓦片的組織則是使用了gltf的思想揽乱,二進(jìn)制幾何數(shù)據(jù)加JSON格式元數(shù)據(jù),這塊不展開說了研叫,目前的情況是OGC采用了I3S锤窑,從渲染效率來說超圖和ESRI的都優(yōu)于Cesium,此處需要說一下超圖的s3m是3d tiles的改進(jìn)嚷炉,框架也是直接在cesium上面改的渊啰。
? ? ? 在代碼方面也是我感興趣的方面,因?yàn)槲覍?duì)JS一直保持著一顆敬畏的心申屹,真心佩服能把JS玩的很溜的大牛X绘证。首先在模塊化方面,Cesium使用AMD的方案就行代碼組織哗讥,這樣一來就可以實(shí)現(xiàn)代碼的按需加載嚷那,對(duì)于像Cesium這種大型的前端程序是很有必要的,盡可能的減少加載的延遲和對(duì)帶寬的占用杆煞。對(duì)于服務(wù)端的部分魏宽,Cesium則使用的是CommoJS的組織方式,這也是NodeJs默認(rèn)使用的模塊方案决乎,服務(wù)端部分則不存在延遲的問題队询,代碼看起來也能優(yōu)雅一點(diǎn),順便吐槽一下AMD那個(gè)define真是挫爆了构诚,其他的在代碼編寫上總結(jié)了如下幾點(diǎn):
1蚌斩、使用盡可能多的參數(shù)來構(gòu)造對(duì)象。JS在我看來就是很隨意范嘱,恰恰是這種很隨意的特性讓每次寫代碼的時(shí)候心里面很心虛送膳,不知道后面VM在搞什么鬼,在Cesium中提倡盡量使用完備的構(gòu)造參數(shù)來進(jìn)行對(duì)象構(gòu)建丑蛤,而不推薦后期直接對(duì)實(shí)例進(jìn)行instance.x= value這種操作叠聋,因?yàn)檫@會(huì)導(dǎo)致實(shí)例的重新分配和屬性的拷貝。
2受裹、直接暴露屬性字段晒奕。在強(qiáng)類型語言中的屬性定義有專門的語法,C#中是get,set;java中是getter, setter方法脑慧,對(duì)于JS來說定義屬性就是Object.defineProperty魄眉,這樣做本質(zhì)上就是將屬性轉(zhuǎn)換為函數(shù)調(diào)用,可以在修改或者返回屬性的時(shí)候做一些控制闷袒,增強(qiáng)程序的健壯性坑律,但是這也恰恰增加了開銷,對(duì)于Cesium而言這都需要小心翼翼囊骤,因而推薦直接暴露公有字段晃择。
3、避免頻繁的gc也物。gc是需要付出代價(jià)的宫屠,在整理內(nèi)存的時(shí)候當(dāng)前線程會(huì)被掛起,對(duì)于交互式的程序如果頻繁gc那還搞個(gè)球滑蚯,對(duì)于webgl程序都會(huì)有個(gè)loop浪蹂,如果在每個(gè)loop里面都進(jìn)行變量的分配那很快就會(huì)觸發(fā)gc,形成很有規(guī)律的大波浪曲線告材,要改善這種情況就只能盡可能的減少變量的分配而是盡可能的利用現(xiàn)有變量坤次,因而Cesium的函數(shù)參數(shù)都會(huì)帶一個(gè)result的變量用來存儲(chǔ)返回值,這樣就能提高變量的利用效率斥赋。
4缰猴、使用Transfer Object進(jìn)行跨線程通訊。說到跨線程必然就是Web Worker了疤剑,由于主線程和worker線程無法共享內(nèi)存滑绒,線程之間的數(shù)據(jù)傳遞只能通過序列化,在js這邊就是json隘膘,這種序列化必然式要拷貝然后傳遞疑故,如果是數(shù)量較大,這種這種拷貝的代價(jià)就需要納入考慮了棘幸,Cesium中使用的方案是使用Transfer Object進(jìn)行跨線程傳輸,使用這種對(duì)象就會(huì)導(dǎo)致傳遞的對(duì)象在sender方不可再訪問了倦零,換來的是性能的提升误续,Cesium再這種場景下都會(huì)將實(shí)例pack到ArrayBuffer然后再post。
? ? ? 好就到這里扫茅,后面會(huì)再看一些關(guān)于調(diào)度方面的東西蹋嵌,有時(shí)間再分享出來。