基於 HTML5 WebGL 的加油站 3D 可視化監控
- 2019 年 10 月 3 日
- 筆記
前言
隨著數字化,工業互聯網,物聯網的發展,中國加油站正向有人值守,無人操作,遠程控制的方向發展,傳統的人工巡查方式逐漸轉變為以自動化控制為主的在線監控方式,即採用數據採集與監控系統 SCADA。SCADA 系統的推廣使用,大大提高了中國加油站的監控效率,本文所講的則是通過對加油站的可視化建模,結合 HT 的 3D 可視化以及 2D 監控面板來實現對加油站的可視化監控。三維可視化監控系統是將三維的可視化技術和數據採集與監控技術融合,充分發揮了兩種技術的核心優勢,並通過資料庫進行數據共享,共同構成一種全新的 SCADA 系統。該系統中也結合了海康的攝影機監控,通過調用海康提供的攝影機地址,實時的將影片流傳輸到前台,並且展示在 2d 頁面上。在真實的系統中,每個加油機以及加油罐都有自己對應需要展示的數據,這個可以根據自己需要展示的內容來設計 2d 面板,之後根據後台傳來的數據進行展示。數據採集與監控系統通過各類的感測器實時採集監控對象的各類數據,上傳資料庫並實時共享給三維可視化技術搭建的監控對象的三維可視化模型及場景,最後通過監控系統直觀的展示出來,極大的提高了監控對象數據的表達能力和工作人員的工作效率。
該系統中實現了對加油機,油罐的監控,以及對加油站內的攝影機進行調取並顯示,本文會講解使用 HT 來搭建該系統的步驟,以及對場景中使用到的部分關鍵程式碼進行說明。
預覽地址:基於 HTML5 WebGL 的加油站 3D 可視化監控 http://www.hightopo.com/demo/gas-station-demo/
介面效果預覽
影片監控效果
加油機監控效果
油罐監控效果
加油站切換效果
因為系統中對許多的加油站進行建模,所以系統中可以根據 url 地址的 stationCode 來區分不同的加油站,即 stationCode 為該加油站的唯一標識,當然該系統可以嵌入到任何第三方的系統中,HT 只需要第三方頁面給一個 dom,就可以將該頁面放到該 dom 中,例如上圖 3d 場景,ht 可以通過 g3d.addToDOM(el) 來將 3d 場景的 dom append 到 el 這個 dom 下,g3d 為 ht 中 ht.graph3d.Graph3dView 的實例,具體可查看 3D 手冊
HT 搭建系統步驟
1.製作模型
在 ht 的 3D 場景中部分簡單的建模可以根據 ht 的 api 來進行搭建,例如牆面,管道,六面體,地板等等基本 3d 模型,例如如果想製作一個三維的球體模型,則可以通過以下程式碼:
1 var node = new ht.Node(); 2 node.s({ 3 "shape3d": "sphere" // 此處指定該 node style 的 shape3d 為 sphere 即球體的意思 4 });
如果需要使用程式碼來進行較為複雜些的建模,則可以通過指定模型的頂點資訊來進行搭建,大體可理解為 3d 中的模型都是由三角面進行拼接而成的,所以指定該模型的所有三角面就可以構造出該模型,三角面又是由三個頂點資訊構成,所以指定模型的頂點資訊也可以構建模型,具體可以參考 建模手冊
但是在我們這個加油站可視化監控系統中,我們的加油機模型以及油罐和加油站外景的模型都是十分複雜的模型,如果採用上述兩種方法:
- 通過第一種簡單的牆面,球體,六面體等模型拼接出加油站是不現實的,因為模型沒有那麼全面,而且貼圖部分也不好分開貼,所以不可行。
- 通過指定頂點資訊來構造加油站的所有模型,這部分雖然在理論上是可行的,但是計算頂點資訊需要大量的工作,可想而知程式碼部分的工作量是不小的,所以不可行。
目前 ht 可以支援 obj 模型的導入,所以設計師可以根據加油站拍攝的外景圖片對加油站場景以及加油機,油罐等的模型進行建模,obj 模型可以使用主流的 3dMAX 等的建模工具進行搭建,之後導入到 ht 中進行顯示。對於系統中需要交互的模型則要分開進行建模,例如加油機模型不可和加油站場景的 obj 模型在同一個 obj 中,例如下圖分開方式:
2.搭建場景
上一步中我們已經得到了場景所需要的所有模型,在 ht 中可以通過 ht.Default.loadObj(objUrl, mtlUrl, params) 來載入 obj 的模型,之後通過 ht.Default.setShape3dModel(name, model) 來註冊模型,loadObj 用來讀取模型的頂點資訊以及貼圖部分的資訊,就是上一步中所指的第二點通過頂點資訊來構造模型,此時頂點資訊已經由 obj 模型提供,所以拿到頂點資訊,貼圖等資訊之後,可以通過 setShape3dModel 來註冊模型,具體使用方法請參考 OBJ 手冊,之後可以通過 ht 的 node 圖元來使用該模型,具體使用方法如下:
1 var node = new ht.Node(); 2 node.s({ 3 "shape3d": name 4 });
上面的 name 就是通過 ht.Default.setShape3dModel(name, model) 中的 name 得到的,表示此圖元使用該模型來展示。
構成該監控系統的還有用來展示加油機,油罐模型的具體監控參數的面板,ht 中所有的 2d 都為矢量,所以放大不會失真,2d 面板也是通過一個個圖元進行擺放展示,通過調整每個圖元的樣式來美化圖元,具體的樣式可參考 風格手冊。
油罐 2d 面板展示如下:
3.對接數據
上一步中我們已經把需要展示的模型以及需要展示的監控數據進行了設計,並且在場景中進行了擺放,所以該步驟中則需要對數據進行對接,目前對接部分包括:
- 2d 面板兩側的數據對接
- 影片監控的對接
第一條的對接可以通過 socket 或者 ajax 來進行,將後台數據傳輸到前台之後動態綁定到介面上顯示即可,ht 中通過數據綁定來驅動介面上內容的動態刷新,具體的綁定操作可以查看 數據綁定手冊。
系統中攝影機監控部分主要是通過將第三方的影片 dom 嵌入到 ht 的圖紙中,ht 中可以通過 renderHTML 來嵌入 dom,嵌入 dom 的原理其實也是在圖紙的對應位置加入一個 node 圖元,根據圖紙的縮放值(zoom),以及橫向偏移值(tx),縱向偏移值(ty) 的圖紙資訊以及該圖元的 position, width, height 的圖元資訊來動態的計算 dom 的寬高和 dom 的位置,具體程式碼可以參考如下:
1 let rect = node.getRect(), // 獲取該 node 的包圍矩形資訊 2 zoom = graphView.getZoom(), // 獲取圖紙的縮放值 3 tx = graphView.tx(), // 獲取圖紙的橫向偏移值 4 ty = graphView.ty(); // 獲取圖紙的縱向偏移值 5 // 下面操作為對 node 的包圍矩形進行縮放 6 rect.x *= zoom; 7 rect.y *= zoom; 8 rect.width *= zoom; 9 rect.height *= zoom; 10 11 // div 的 left 即為下面的 x 坐標, top 即為下面的 y 坐標 12 let x = tx + rect.x; 13 let y = ty + rect.y; 14 15 div.style.position = 'absolute'; 16 div.style.width = rect.width + 'px'; 17 div.style.height = rect.height + 'px'; 18 div.style.left = x + 'px'; 19 div.style.top = y + 'px';
上面程式碼展示了動態擺放 dom 到 ht 圖紙的原理,所以用戶可以根據自己的需求將 dom 元素放到 2d 圖紙中去,該系統中的監控模組 dom 就是通過該方式的原理進行動態擺放,從下圖可以看出 dom 疊加的效果。
4.製作動畫效果
該系統中動畫效果比較簡單,主要就是點擊加油機或者油罐時,將視角飛向該物體,並且二維面板上顯示對應的監控數據,視角的切換主要是修改 3D 場景的 eye 以及 center 的數據,但是 ht 中提供了更為方便的操作函數 flyTo,所以主要程式碼即為下面一行:
1 // node 即為要飛向的節點 例如加油機 2 // 第二參數為配置參數 3 g3d.flyTo(node, { animation: true, direction: [-16, 6, 8], distance: 600 });
上述第二個參數具體可參考上述所提供的 3D 手冊,direction:默認undefined,眼睛處於目標的方向(相對目標,受到目標自身旋轉影響,distance :默認undefined(未定義的話則使用下面的ratio模式計算距離),浮點類型,表示眼睛跟中心的固定距離,上述所用到的兩個參數解釋即為此。
該函數還有一個使用方法為當第一參數傳值為 null 空時,視角會調整看向場景內所有節點,所以利用此功能可能看到加油站的全景。
系統中還有一個效果是虛化背景,虛化背景的原理就是修改場景中所有模型的透明度,例如該系統中通過遍歷所有節點,將當前節點的透明度設置為 0.1,則在視覺上我們看到的場景即為虛化的場景,具體節點的樣式屬性可以參考上面已經給出的風格手冊,關鍵程式碼如下:
1 // 遍歷場景中所有圖元 2 dataModel.each((d) = >{ 3 // opacityMap 用來記錄當前某個節點沒有虛化之前的透明度值 4 if (!opacityMap[d.getId()]) { 5 opacityMap[d.getId()] = { 6 'shape3d.opacity': d.s('shape3d.opacity'), 7 'shape3d.transparent': d.s('shape3d.transparent'), 8 'all.opacity': d.s('all.opacity'), 9 'all.transparent': d.s('all.transparent'), 10 'left.opacity': d.s('left.opacity'), 11 'left.transparent': d.s('left.transparent'), 12 'right.opacity': d.s('right.opacity'), 13 'right.transparent': d.s('right.transparent'), 14 'front.opacity': d.s('front.opacity'), 15 'front.transparent': d.s('front.transparent'), 16 'back.opacity': d.s('back.opacity'), 17 'back.transparent': d.s('back.transparent'), 18 'top.opacity': d.s('top.opacity'), 19 'top.transparent': d.s('top.transparent'), 20 'bottom.opacity': d.s('bottom.opacity'), 21 'bottom.transparent': d.s('bottom.transparent'), 22 '3d.selectable': d.s('3d.selectable') 23 }; 24 } 25 // 設置當前節點的透明度 opacity 為需要虛化至多大透明度 系統中為 0.1 26 d.s({ 27 'shape3d.opacity': opacity, 28 'shape3d.transparent': true, 29 'all.opacity': opacity, 30 'all.transparent': true, 31 'left.opacity': opacity, 32 'left.transparent': true, 33 'right.opacity': opacity, 34 'right.transparent': true, 35 'front.opacity': opacity, 36 'front.transparent': true, 37 'back.opacity': opacity, 38 'back.transparent': true, 39 'top.opacity': opacity, 40 'top.transparent': true, 41 'bottom.opacity': opacity, 42 'bottom.transparent': true, 43 '3d.selectable': false 44 }); 45 });
上面程式碼執行之後場景中所有的節點就被虛化,因為每個節點虛化所需要設置的透明度屬性不同,所以一共有上面十幾種樣式屬性需要判斷設置,具體的樣式名稱可以參考上文提出的風格手冊,以下為虛化效果:
場景中有雙擊便利店進入便利店內景的操作,具體交互以及效果如下圖:
5.優化場景
當 3d 場景中點或者面的數量較多時,3d 面板,公告板部分較多時,ht 中有幾種優化策略可以進行優化,我們知道 3d 場景中的所有模型都是由三角面構成的,而三角面又是由三個頂點構成的,所以如果場景中的點或者面比較多的時候場景會出現一定的卡頓,GPU 渲染會比較費時,在 ht 中可以通過在控制台輸入 g3d.showDebugTip() 來顯示當前場景一共有多少面和頂點,具體效果如下:
其中 Vertices 為點的數量,Faces 為面的數量。
所以在 ht 中可以有以下 4 種直觀優化策略可以優化:
- 減少模型的面數
- 使用批量處理場景中大量的相同圖元
- 對 3d 面板使用快取
- 當場景視角距離較遠時隱藏部分細節圖元,或者當場景視角距離某個模型很近時,隱藏看不見的圖元以提高性能
第一種情況可以在設計建模時通過各種減面的手段來減少模型的面數,這一部分優化的空間是最大的,也是效果最明顯的。
第二種情況可以使用批量,批量能提高性能的原理在於,當圖元一個個獨立繪製模型時性能較差,而但一批圖元聚合成一個大模型進行一次性的繪製時, 則會極大提高WebGL刷新性能,具體可參考 批量手冊。
第三種情況使用 shape3d.image.cache 這個屬性來開啟面板的快取,當我們一個場景中如果需要使用大量的類似公告板的功能,我們可以利用上面的屬性對該節點設置快取,具體使用方法可以參考 3D手冊。
第四種情況我們可以在眼睛距離場景很遠的時候隱藏部分細節圖元,類似地圖縮放到很小的時候,具體的城市會隱藏掉,放大到具體模型細節時,其它看不見的圖元可以相應設置隱藏,這樣可以提高不少的性能。
手機端效果