:從S3M發(fā)布到性能調(diào)優(yōu))
簡介面向GIS開發(fā)與前端可視化領(lǐng)域一份基于SuperMap iClient3D for Cesium與Vue的三維地圖工程資源重點解決傾斜攝影數(shù)據(jù)在Web端的加載與渲染問題。項目源碼包含完整src目錄、Vue單文件組件、路由與交互邏輯并配置vue.config.js、babel.config.js、package.json等工程化文件數(shù)據(jù)側(cè)提供S3M傾斜攝影模型、地形/影像圖層及png/jpg界面截圖可作為從零搭建三維GIS應(yīng)用的參考模板。資源共560個文件RAR壓縮包約25.84MB其中js為業(yè)務(wù)邏輯與地圖交互vue為組件結(jié)構(gòu)json為配置與圖層數(shù)據(jù)css負責樣式s3m為傾斜攝影模型html為頁面入口另有大量png/jpg圖片素材目錄劃分清晰便于二次開發(fā)時快速定位所需模塊。目前已吸引721人學(xué)習(xí)下載對希望掌握SuperMap與Cesium集成方式、理解傾斜攝影數(shù)據(jù)組織與場景發(fā)布流程的前端或GIS工程師具有很強的直接參考價值。 先說結(jié)論SuperMap iClient3D for Cesium這套東西本質(zhì)上就是把Cesium的能力和超圖自己的數(shù)據(jù)服務(wù)串起來你只需要搞清楚數(shù)據(jù)從哪來、坐標系對不對、加載入口在哪剩下的基本就是調(diào)參和踩坑。這篇文章我拿一個實際的傾斜攝影項目展開把從場景初始化到圖層加載、再到性能和問題排查的全過程講透適合正在做GIS三維可視化、剛接觸SuperMapCesium這套技術(shù)棧的開發(fā)者直接參考。1. 項目背景與整體思路1.1 為什么選SuperMap iClient3D for Cesium而不是裸Cesium很多朋友一上來會問Cesium本身就是開源的直接加載3D Tiles不就行了為什么還要再多套一層SuperMap這個問題的答案得看你的數(shù)據(jù)從哪來。如果你的傾斜攝影成果是一堆OSGB文件或者是CCContextCapture導(dǎo)出的模型目錄裸Cesium確實能通過3D Tiles格式加載但這里面有幾個麻煩事數(shù)據(jù)體積大得離譜一個城中村的傾斜模型動輒幾個GB瀏覽器直接撐不住。原生3D Tiles對超圖iServer發(fā)布的REST三維服務(wù)沒有原生支持你需要自己寫服務(wù)端切片邏輯。坐標基準、LOD層級、紋理壓縮這些事全得自己手動處理工作量非??捎^。SuperMap iClient3D for Cesium幫你把這些封裝好了。它保留了大部門Cesium原生API的寫法同時擴展了對S3M格式的支持。S3M這個格式你可以理解成三維世界里的瓦片金字塔——傾斜攝影數(shù)據(jù)會按區(qū)域、按層級切成很多小塊瀏覽器端只加載當前視野內(nèi)、當前精度需要的那些塊而不是一股腦全塞進內(nèi)存。這樣一來幾個GB的數(shù)據(jù)也能在網(wǎng)頁上流暢轉(zhuǎn)這是裸Cesium加普通OSGB很難做到的效果。1.2 傾斜攝影數(shù)據(jù)從采集到發(fā)布要經(jīng)過幾個階段傾斜攝影不是一個格式而是一條完整的生產(chǎn)鏈路。我在項目中拿到的原始數(shù)據(jù)其實是無人機帶著五鏡頭相機一個下視、四個側(cè)視飛一圈拍回來的照片然后經(jīng)過三維重建算法生成帶真實紋理的三角網(wǎng)模型。這個過程中有幾個關(guān)鍵節(jié)點你拿到手的成果往往已經(jīng)經(jīng)過了前兩步但你要清楚它們是怎么來的采集階段無人機按航線飛行相機每隔幾秒拍一組照片。這個階段的產(chǎn)物是一堆帶有定位姿態(tài)信息的照片通常在幾千到幾萬張。重建階段用ContextCapture、大疆智駕或者重建大師這類軟件把照片跑成OSGB格式的三維模型。OSGB是傾斜攝影領(lǐng)域最通用的中間格式它的特點是分塊存儲每個塊是一個獨立的文件夾里面包含了模型數(shù)據(jù)和一個記錄坐標原點信息的json文件。轉(zhuǎn)換階段超圖系的做法是用iDesktop或者iServer的數(shù)據(jù)處理工具把OSGB轉(zhuǎn)成S3M格式。S3M是SuperMap定義的三維空間數(shù)據(jù)格式專為Web端流式加載設(shè)計自帶LOD分層、紋理壓縮和空間索引。發(fā)布階段把S3M數(shù)據(jù)發(fā)布到SuperMap iServer上得到一個三維服務(wù)的REST地址前端用iClient3D for Cesium去請求這個地址就能在瀏覽器里加載顯示。我在項目中踩過最深的坑是直接從CC導(dǎo)出OSGB然后扔給前端加載結(jié)果瀏覽器卡死、白屏、內(nèi)存溢出輪著來。后來老老實實走完轉(zhuǎn)換和發(fā)布這兩步頁面直接起飛。這個流程上的經(jīng)驗建議大家一開始就繞開。2. 三維場景初始化與基礎(chǔ)環(huán)境搭建2.1 依賴引入與Viewer初始化要在項目里用SuperMap iClient3D for Cesium前提是你本機已經(jīng)裝好了SuperMap iServer并且用iDesktop把傾斜攝影數(shù)據(jù)轉(zhuǎn)成了S3M格式發(fā)布成了三維服務(wù)。如果這一步還沒做后面所有代碼都是空中樓閣。常見的做法是先在iDesktop里導(dǎo)入OSGB數(shù)據(jù)右鍵生成S3M瓦片然后在iServer里新建一個三維服務(wù)把S3M數(shù)據(jù)的配置文件路徑填進去。前端部分SuperMap iClient3D for Cesium的引入方式跟Cesium很像。我建議直接用它的CDN包或者把整個包下載下來放到項目靜態(tài)目錄里。頁面里的核心引入是這樣的link href./Build/Cesium/Widgets/widgets.css relstylesheet / script src./Build/Cesium/Cesium.js/script script src./Build/SuperMap-3D/iclient3d.min.js/script注意腳本的加載順序先加載Cesium核心庫再加載SuperMap的擴展庫。順序反了viewer.scene.addS3MTilesLayerByUrl這類擴展API就找不到頁面會報TypeError: viewer.scene.addS3MTilesLayerByUrl is not a function。初始化Viewer的代碼長這樣var viewer new Cesium.Viewer(cesiumContainer, { // 如果只做三維場景可以把三維和二維切換的按鈕關(guān)掉 sceneModePicker: false, // 圖層選擇器在加載S3M后意義不大 baseLayerPicker: false, // 時間軸、動畫控件對純GIS項目無用 timeline: false, animation: false, // 模型加載后的屬性面板一般也用不上 infoBox: false, // 關(guān)閉抗鋸齒開關(guān)性能敏感的項目可以開 msaa: 4, // 讓地球不顯示默認的天空盒子方便看傾斜模型 skyBox: false, // 背景色調(diào)成黑色視覺上更干凈 baseLayer: Cesium.ImageryLayer.fromProviderAsync( Cesium.TileMapServiceImageryProvider.fromUrl( Cesium.buildModuleUrl(Assets/Textures/NaturalEarthII) ) ) });這里有個細節(jié)很多人初始化完Viewer之后發(fā)現(xiàn)地球表面是一張大藍底圖加載傾斜攝影上去以后模型和底圖混在一起顯得很臟。我一般會把baseLayer換成一張不帶影像的純色底圖或者干脆用viewer.imageryLayers.removeAll()把默認圖層清掉只留傾斜模型作為主要視覺對象。2.2 場景基礎(chǔ)參數(shù)的設(shè)置要點Viewer初始化完成之后有幾個場景級參數(shù)我建議在加載數(shù)據(jù)之前就設(shè)好不然等你數(shù)據(jù)加載到一半再調(diào)整很容易出現(xiàn)閃爍、黑屏這類莫名其妙的問題。相機默認高度和視角傾斜攝影數(shù)據(jù)通常在地表一個相對集中的范圍內(nèi)如果直接沿用Cesium默認的全球視角你打開頁面看到的只是一顆藍色星球根本看不到模型。需要設(shè)置相機位置讓它飛到數(shù)據(jù)所在的區(qū)域。這個在下一節(jié)講圖層定位時會詳細展開。光照設(shè)置Cesium默認是開啟了太陽光照的但傾斜攝影的紋理顏色會受光照角度影響同一個模型在上午和下午看起來色調(diào)不一樣。如果項目對顏色一致性有要求可以把光照固定在一個方向或者直接調(diào)整場景的照明參數(shù)viewer.scene.globe.enableLighting true; // 或者固定光照方向 viewer.scene.globe.light new Cesium.DirectionalLight({ direction: new Cesium.Cartesian3(-0.5, 0.5, 1.0), color: Cesium.Color.WHITE });坐標系的坑Cesium底層用的是WGS84橢球體但國內(nèi)傾斜攝影成果經(jīng)常用CGCS2000坐標系。雖然絕大多數(shù)情況下兩者在可視化層面偏差不大但如果你發(fā)現(xiàn)模型和底圖錯位了幾個像素到幾十米第一件事就是確認數(shù)據(jù)發(fā)布時用的坐標系而不是去調(diào)相機。3. 傾斜攝影圖層加載核心實現(xiàn)3.1 用iServer發(fā)布S3M服務(wù)的前置配置在寫前端代碼之前你先得在iServer里把服務(wù)跑起來。打開iServer的管理界面新建一個三維服務(wù)數(shù)據(jù)來源選擇S3M數(shù)據(jù)的配置文件。注意這里選的是.s3m生成的目錄配置文件夾不是單個模型。我見過有同事把OSGB目錄直接當成數(shù)據(jù)目錄填進去結(jié)果服務(wù)報錯半天沒找到原因。發(fā)布成功之后在瀏覽器里訪問服務(wù)的地址比如http://localhost:8090/iserver/services/3D-tilt/rest/realspace如果能看到數(shù)據(jù)列表說明服務(wù)沒問題。前端要去請求的通常是一個datas下的具體數(shù)據(jù)項類似這樣http://localhost:8090/iserver/services/3D-huancheng/rest/realspace/datas/huancheng3.2 核心加載代碼與逐行解析S3M圖層的加載是SuperMap擴展庫封裝好的一個方法實際調(diào)用非常簡單但背后其實做了很多事。最基礎(chǔ)的寫法如下var targetLayer viewer.scene.addS3MTilesLayerByUrl( http://localhost:8090/iserver/services/3D-huancheng/rest/realspace/datas/huancheng, { name: 環(huán)城村傾斜模型 } );addS3MTilesLayerByUrl返回的是圖層的Promise對象在這個Promise對象里你可以拿到加載完成后的圖層實例從而做定位、顯隱控制、透明度調(diào)整等操作。再往細了說我項目中用的更完整的方法是var promise viewer.scene.addS3MTilesLayerByUrl( http://localhost:8090/iserver/services/3D-huancheng/rest/realspace/datas/huancheng, { name: 環(huán)城村傾斜模型, // 關(guān)鍵參數(shù)決定LOD切換的閾值數(shù)字越小模型越精細但加載壓力越大 maximumScreenSpaceError: 16, // 模型加載完成后是否自動飛行定位過去 autoReset: true, // 是否裁剪到數(shù)據(jù)邊界防止模型跑到視野外 cull: true } ); promise.then(function(layer) { // 拿到圖層對象存到全局變量方便后續(xù)控制 window.tiltLayer layer; // 把相機飛到模型所在位置 viewer.flyTo(layer); });這個maximumScreenSpaceError參數(shù)是傾斜攝影加載效果好壞的關(guān)鍵之一。它表示當前渲染誤差的閾值數(shù)值越小瀏覽器越傾向于加載高精度的瓦片模型拖近了看棱角越分明但請求量會成倍增加。數(shù)值調(diào)大加載性能變好但模型拉近會模糊。我習(xí)慣在項目調(diào)試階段先設(shè)20看整體流暢度再逐檔往下壓到16左右是一個平衡點。autoReset參數(shù)建議開著它會在模型加載完后自動把相機挪到模型所在位置省去你手動計算坐標的功夫。3.3 模型姿態(tài)調(diào)整與坐標定位如果你在Cesium里加載3D模型繞不開heading、pitch、roll這組姿態(tài)參數(shù)。S3M傾斜攝影圖層也有對應(yīng)的姿態(tài)控制接口。為什么需要這個因為傾斜攝影數(shù)據(jù)在重建和轉(zhuǎn)換過程中偶爾會丟失部分坐標信息或者模型的北方向和真實地理北方向存在偏差。我在一個廠房項目里就遇到過模型加載出來整個是斜著放倒在地面上的一開始以為是數(shù)據(jù)和坐標系問題排查了半天才發(fā)現(xiàn)是模型的roll軸偏了。調(diào)整姿態(tài)的代碼如下if (window.tiltLayer) { // 獲取模型可調(diào)整的所有參數(shù) var layerInfo window.tiltLayer.getLayerInfo(); // 修改姿態(tài)繞X軸旋轉(zhuǎn) window.tiltLayer.rotateX(0.5); window.tiltLayer.rotateY(0); window.tiltLayer.rotateZ(0); }但這里要提醒一句不要動不動就去旋轉(zhuǎn)模型。傾斜攝影成果一般自帶刺點信息和地理配準正常情況下加載進去就是正的。如果你發(fā)現(xiàn)模型位置不對先查坐標系和服務(wù)發(fā)布配置確認是姿態(tài)問題再去做旋轉(zhuǎn)。人為去旋轉(zhuǎn)相當于在錯誤的基礎(chǔ)上打補丁治標不治本而且后期跟其他數(shù)據(jù)疊加還會出更多問題。相機手動定位到模型區(qū)域的寫法很常用viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(116.3912, 39.9072, 500), orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-45), roll: 0 } });這里的fromDegrees三個參數(shù)分別是經(jīng)度、緯度、高度單位是度/米。如果你知道模型中心點的經(jīng)緯度就可以用這種方式快速定位視野。不過用viewer.flyTo(layer)會更省事它會自動計算模型包圍盒的中心點。4. 場景優(yōu)化與性能調(diào)優(yōu)4.1 LOD精度與加載壓力的取舍傾斜攝影數(shù)據(jù)量非常大未經(jīng)處理的OSGB可能包含上百萬個三角形面片。S3M格式之所以能在瀏覽器里跑起來核心依賴LODLevel of Detail機制遠處看模型時只加載低精度粗略輪廓鏡頭拉近時再加載高精度細節(jié)。這個機制生效的程度直接受maximumScreenSpaceError控制。我實測過把參數(shù)從20降到8模型細節(jié)會非常清晰甚至能看到墻面上的裂縫和管道走向但代價是瀏覽器需要同時加載的瓦片數(shù)量激增低配電腦大概率會卡頓或者崩潰。反過來調(diào)大到32頁面很流暢但模型從遠處看會像打了馬賽克一樣模糊。我的建議是分場景來設(shè)大范圍城市級瀏覽設(shè)24到32優(yōu)先保證流暢度。單棟建筑或小區(qū)級精細查看設(shè)10到16犧牲一點流暢度換細節(jié)。如果項目既有大范圍漫游又有局部精細測量需求可以考慮用多個圖層分精度發(fā)布或者通過鼠標事件動態(tài)調(diào)參。4.2 渲染性能與視覺增強的實操方法除了LOD調(diào)節(jié)我還在項目里做了幾件事來提升體驗。一個是關(guān)閉不需要的Cesium默認特效。默認的天空盒、霧氣效果、動態(tài)光照在展示傾斜模型時沒必要全部開啟我會把viewer.scene.sun.show設(shè)為false同時關(guān)閉大氣效果viewer.scene.skyAtmosphere.show這樣頁面干凈很多。另一個是開啟GPU抗鋸齒。如果你的顯卡支持viewer.scene.msaaSamples 4能明顯改善模型邊緣的鋸齒感對傾斜攝影這類高密度紋理效果更明顯。不過這個參數(shù)對性能有影響中低端設(shè)備會感到幀率下降。如果項目需要疊加一些動態(tài)特效比如雷達掃描、可視域分析、天際線分析Cesium的Material接口和SuperMap擴展庫也能滿足。我在一個園區(qū)安防項目里做過一個雷達掃描效果核心思路是利用一個圓形的紋理作為材質(zhì)的uniform值每幀更新圓心角度做出掃描線的效果。雖然傾斜攝影本身是靜態(tài)的但疊加這些特效之后整個場景的質(zhì)感和用途立刻不一樣了。關(guān)于衛(wèi)星圖和傾斜模型的配準問題如果頁面里同時疊加天地圖或者離線影像瓦片做底圖可能發(fā)現(xiàn)模型和底圖邊緣對不齊。這種情況大部分是投影坐標系或者影像分辨率導(dǎo)致的偏差可以嘗試把底圖影像圖層調(diào)整透明度做視覺校準// 給圖層設(shè)置透明度用來對比模型與影像的吻合程度 var imageryLayer viewer.imageryLayers.addImageryProvider(imageryProvider); imageryLayer.alpha 0.5;微調(diào)完再恢復(fù)1.0不透明度項目效果會好很多。4.3 離線環(huán)境下加載本地瓦片有些項目對網(wǎng)絡(luò)環(huán)境有要求不能訪問外網(wǎng)。SuperMap體系下完全可以用iServer發(fā)布本地離線瓦片服務(wù)前端代碼不用改只是把服務(wù)地址換成本機或內(nèi)網(wǎng)的地址就行。如果是S3M數(shù)據(jù)本身就存在本機iServer發(fā)布本地數(shù)據(jù)源是天然支持的。影像底圖方面可以用SuperMap iDesktop切一個本地影像瓦片包發(fā)布成REST地圖服務(wù)然后通過Cesium的UrlTemplateImageryProvider去加載。這里的關(guān)鍵點是URL模板的拼接規(guī)則比如var provider new Cesium.UrlTemplateImageryProvider({ url: http://localhost:8090/iserver/services/map-china/rest/maps/ChinaMap/tileMapServer/tile/{z}/{x}/{y}.png });這套方案我在一個內(nèi)網(wǎng)部署的三維會商系統(tǒng)里用過穩(wěn)定跑了幾個月沒出過問題。5. 常見問題與排查經(jīng)驗實錄5.1 高頻問題速查表我把項目中遇到過的、以及周圍同行問過的高頻問題整理成下表你可以直接對照排查現(xiàn)象常見原因解決辦法頁面加載后地球是空的看不到模型相機沒定位到模型范圍檢查圖層加載成功后是否調(diào)用了flyTo或者手動設(shè)置相機經(jīng)緯度高度控制臺報錯addS3MTilesLayerByUrl is not a function擴展庫沒加載或者加載順序錯確認iclient3d.min.js在Cesium.js之后引入模型加載后是黑色的光照沒開或者場景參數(shù)被改檢查enableLighting是否為true或者給場景加一個固定光源模型和底圖錯位明顯坐標系不一致確認S3M發(fā)布時的坐標系避免WGS84和CGCS2000混用拉近之后模型模糊、紋理不清LOD參數(shù)過大調(diào)小maximumScreenSpaceError到16以下頁面加載卡頓、內(nèi)存占用飆升數(shù)據(jù)量超出瀏覽器承受范圍檢查是否加載了過多圖層或者LOD參數(shù)被設(shè)得太小模型部分區(qū)域消失、像被啃掉一塊瓦片數(shù)據(jù)缺失或者請求失敗檢查iServer服務(wù)日志確認原始OSGB數(shù)據(jù)完整性5.2 排查思路與避坑經(jīng)驗排查傾斜攝影加載問題我最大的經(jīng)驗是從數(shù)據(jù)端到前端按順序查不要一上來就懷疑代碼。前端的問題通常表現(xiàn)為代碼報錯但傾斜攝影加載失敗往往不是代碼邏輯錯而是數(shù)據(jù)服務(wù)沒起來。我見過同事花了整整一個下午調(diào)試前端代碼最后發(fā)現(xiàn)iServer的三維服務(wù)根本就沒啟動成功數(shù)據(jù)地址返回的是404。所以如果你遇到模型加載不出來第一步先訪問一下服務(wù)URL看看能不能正常返回數(shù)據(jù)列表。這一步能篩掉一半問題。第二個常見坑是S3M數(shù)據(jù)在一臺機器上能加載換到另一臺機器就白屏。這種情況大概率是跨域問題。iServer在前端請求的時候如果前端頁面和iServer不在一臺機器上需要在iServer的配置里開啟跨域支持或者在部署時用Nginx做反向代理統(tǒng)一同源。我項目里這兩個方案都用過Nginx那個更穩(wěn)順便把靜態(tài)資源也一并代理了。第三個坑是傾斜攝影數(shù)據(jù)更新之后瀏覽器和iServer的緩存導(dǎo)致看到的還是舊數(shù)據(jù)。我在一個周更數(shù)據(jù)的項目中遇到過數(shù)據(jù)明明重新發(fā)布了但前端頁面怎么刷新都是舊模型。最后發(fā)現(xiàn)是iServer服務(wù)端有緩存需要在iServer管理界面里清緩存并重啟數(shù)據(jù)服務(wù)。前端這邊也可以用viewer.scene.primitives.removeAll()清理掉舊的圖元再重新加載。第四個注意事項是在本地文件系統(tǒng)下直接雙擊HTML文件打開頁面大概率會遇到CORS限制加載不出三維數(shù)據(jù)。建議任何SuperMap三維項目開發(fā)時都用HTTP服務(wù)來跑前端頁面比如用Python的http.server、Node的serve或者直接用VS Code的Live Server插件都行。寫在最后這個項目做到后面我最深的一個體會是SuperMap iClient3D for Cesium并不難上手難的是把傾斜攝影數(shù)據(jù)這條鏈路搞明白。數(shù)據(jù)從無人機采集到CC重建、從OSGB轉(zhuǎn)S3M、從iServer發(fā)布再到前端加載每一步都會埋下各種細枝末節(jié)的坑但只要你把坐標系、服務(wù)地址、LOD參數(shù)這三件事盯死整個流程基本就能跑通。最后再分享一個小技巧如果你調(diào)試時需要頻繁調(diào)整相機位置去看模型的某個角建議在代碼里臨時加一個鍵盤事件用上下左右方向鍵控制相機的moveForward和moveRight比每次手動改經(jīng)緯度快得多。調(diào)試完刪掉就行。這套三維可視化方案后續(xù)還可以平滑地接上視域分析、通視分析、量測工具對智慧城市、園區(qū)管理、礦山監(jiān)測這類項目來說擴展空間非常充裕。本文還有配套的精品資源點擊獲取