基於 HTML5 WebGL 的地鐵站 3D 可視化系統

前言

工業互聯網,物聯網,可視化等名詞在我們現在信息化的大背景下已經是耳熟能詳,日常生活的交通,出行,吃穿等可能都可以用信息化的方式來爲我們表達,在傳統的可視化監控領域,一般都是基於 Web SCADA 的前端技術來實現 2D 可視化監控,本系統採用 Hightopo 的 HT for Web 產品來構造輕量化的 3D 可視化場景,該 3D 場景從正面展示了一個地鐵站的現實場景,包括地鐵的實時運行情況,地鐵上下行情況,視頻監控,煙霧報警,電梯運行情況等等,幫助我們直觀的瞭解當前的地鐵站。

系統中爲了幫助用戶更直觀友好的瀏覽當前地鐵站,提供了三種交互模式:

  • 第一人稱模式 -- 操作就類似行人或車在行進的效果,可以通過鍵盤鼠標控制前進後退。
  • 自動巡檢模式 -- 該模式下用戶不需要任何操作,場景自動前進後退來巡查當前地鐵站的場景。
  • 鼠標操作模式 -- 左鍵旋轉場景,右鍵平移場景。

本篇文章通過對地鐵站可視化場景的搭建,動畫代碼的實現,交互模式的原理解析,以及主要功能點的實現進行闡述,幫助我們瞭解如何使用 HT 實現一個簡單的地鐵站可視化。

預覽地址:基於 HTML5 WebGL 的地鐵站 3D 可視化系統 http://www.hightopo.com/demo/...

界面簡介及效果預覽

地鐵運行效果

圖片描述

地鐵從站外開到站內的效果爲透明度逐漸增加,速度逐漸降低。

漫遊效果

圖片描述

上述爲自動巡檢的漫遊效果,場景自動進行前進旋轉。

監控設備交互效果

圖片描述

當我們點擊場景中的監控設備時可以查看當前設備的運行情況,運行數據等信息。

場景搭建

該系統中的大部分模型都是通過 3dMax 建模生成的,該建模工具可以導出 obj 與 mtl 文件,在 HT 中可以通過解析 obj 與 mtl 文件來生成 3d 場景中的所有複雜模型,當然如果是某些簡單的模型可以直接使用 HT 來繪製,這樣會比 obj 模型更輕量化,所以大部分簡單的模型都是採用 HT for Web 產品輕量化 HTML5/WebGL 建模的方案,具體的解析代碼如下:

 // 分別爲 obj 文件地址,mtl 文件地址
 ht.Default.loadObj('obj/metro.obj', 'obj/metro.mtl', {                    
     center: true, // 模型是否居中,默認爲 false,設置爲 true 則會移動模型位置使其內容居中
     r3: [0, -Math.PI/2, 0], // 旋轉變化參數,格式爲 [rx, ry, rz]
     s3: [0.15, 0.15, 0.15], // 大小變化參數,格式爲 [sx, sy, sz]
     finishFunc: function(modelMap, array, rawS3) {
         if(modelMap) {                                           
              ht.Default.setShape3dModel('metro', array); // 註冊一個名字爲 metro 的模型
         } 
     }
});

上面通過加載 obj 模型之後註冊了一個名字爲 metro 的模型,之後如果要使用該模型可以通過以下代碼來實現:

var node = new ht.Node();
node.s({
    'shape3d': 'metro'
});

上面代碼新建了一個 node 對象,通過設置 style 對象的 shape3d 屬性可以把模型名稱爲 metro 用到該 node 對象上去,之後便是我們場景中看到的地鐵列車模型。

動畫代碼分析

地鐵動畫代碼的實現分析

場景中地鐵的運行是通過 HT 提供的調度插件來實現,調度的具體用法可以參考 HT for Web 的調度手冊,該調度主要用於在指定的時間間隔進行函數回調處理,回調函數的第一個參數爲 data 圖元,也就是 3D 場景中的模型節點,我們可以判斷當前 data 是否爲我們剛纔創建的 metro 那個節點來進行後續的操作,場景中模擬了一個左開的地鐵和一個右開的地鐵,兩輛地鐵會交替出現。在 3D 場景中肯定會有座標系,HT 中是用 x, y, z 來分別表示三個軸,所以地鐵的運動肯定是改變地鐵在座標系中的位置來實現地鐵的運行,地鐵座標如下圖所示:

圖片描述

通過上圖可以知道地鐵在 3D 場景中的座標系,如果要實現地鐵的移動則只需要將地鐵往圖中所示紅色箭頭的方向進行移動,即 x 軸的方向,通過 setX 這個方法不斷的修改地鐵的位置達到地鐵行進的目的,代碼中通過 getSpeedByX 以及 getOpacityByX 兩個方法來不斷獲取此時的列車速度以及列車透明度,以下爲關鍵代碼實現:

let metroTask = {
     interval: 50, // 每五十秒執行一次
     action: (data) => { // 即上文所提回調函數
          // 判斷當時傳進來的節點是否爲地鐵列車節點
         if(data === currentMetro) { 
              // 獲取地鐵此時的 X 軸位置以及行進的方向
             let currentX = data.getX(), direction = data.a('direction'); 
             // 根據當前的 X 軸位置獲取當前的列車速度
             let speed = this.getSpeedByX(currentX); 
             // 根據當前的 X 軸位置獲取當前的列車透明度
             let opacity = this.getOpacityByX(currentX);
             // 判斷此時 X 軸位置是否超過某個值 即地鐵是在某個範圍內移動
             if(Math.abs(currentX) <= 5000) {
                    // 設置當前的透明度
                   opacity !== 1 ? currentMetro.s({ 'shape3d.transparent': true, 'shape3d.opacity': opacity }) :
                                   currentMetro.s({ 'shape3d.transparent': false});
                   // 設置當前的 X 軸位置
                   data.setX(currentX + direction * speed);
                   // 判斷此時地鐵的速度爲 0,所以此時應該執行開門的動畫
                   if(speed === 0) this.doorAnimation(currentMetro, direction);
             }
             // 右方向地鐵開到頭,進行復位
             if(currentX > 5000 && direction === 1) {
                   currentMetro = leftMetro;
                   currentMetro.setX(5000);
             }
             // 左方向地鐵開到頭,進行復位
             if(currentX < -5000 && direction === -1) {
                   currentMetro = rightMetro;
                   currentMetro.setX(-5000);
             }
         }
     }        
};
dm3d.addScheduleTask(metroTask);

通過以上代碼可以知道地鐵在運行的過程中,主要通過修改地鐵的 x 軸位置來產生前進的動畫,並且需要讓地鐵在某個區間內進行運動,需要判斷邊界,而且爲了模擬出真實的效果需要根據地鐵當前的位置不斷獲取當前的列車速度以及列車透明度,以下爲流程圖:

圖片描述

上圖所示的爲地鐵進站時候的流程,當地鐵停靠完畢關門後需要進行出站,此時我們只需要把地鐵位置重新設置一下不爲 0 即可,以下爲部分代碼實現:

currentMetro.setX(direction * 10); // 設置出站列車的位置

當執行上面那句代碼之後上方的 metroTask 調度任務執行到 getSpeedByX 這個方法之後獲取到的 speed 速度不爲 0,因此此時會繼續執行地鐵行進的動畫,此時的速度就是由慢至快,透明度由深至淺。以下爲開門動畫執行流程:

圖片描述

自動巡檢代碼的實現分析

系統中自動巡檢的實現主要是通過修改 3D 場景中的 eye 以及 center 的值,HT 中提供了 rotatewalk 兩個方法來控制視角的旋轉以及視角的行進,rotate 方法在非第一人稱模式時,旋轉是以 center 爲中心進行旋轉,也就是圍繞中心物體旋轉,當爲第一人稱時旋轉以 eye 爲中心進行旋轉,也就是旋轉眼睛朝向方向。walk 函數同時改變 eyecenter 的位置,也就是 eyecenter 在兩點建立的矢量方向上同時移動相同的偏移量。該系統中我沒有采用 rotate 函數而是自己實現了視角的旋轉,因爲原本的 rotate 函數旋轉某個角度會馬上旋轉過去而不會有一個旋轉的過程,所以我重新實現了旋轉的方法,該系統中視角旋轉是通過不斷修改 center 的數值來實現,具體實現過程原理如下圖所示:

圖片描述

部分實現代碼如下:

rotateStep() {
    // 即上圖輔助點 C
    let fromCenter = this.fromCenter; 
    // 即上圖 B 點
    let toCenter = this.toCenter;
    // 每幀轉一度
    let rotateValue = this.rotateFrame || Math.PI / 180; 
    // 輔助點 C 與 B 點之間建立一個方向向量
    let centerVector = new ht.Math.Vector2(toCenter.x - fromCenter.x, toCenter.y - fromCenter.y);
    let centerVectorLength = centerVector.length();
    // 此時旋轉百分比
    let rotatePercent = rotateValue * this.stepNum / this.curRotateVal;
    if(rotatePercent >= 1) {
       rotatePercent = 1;
       this.stepNum = -2;
    }
    let newLength = rotatePercent * centerVectorLength;
    centerVector.setLength(newLength);
    let newCenterVector = centerVector.add(fromCenter);
    // 獲取旋轉過程中 center 的點信息
    let newCenterPosition = [newCenterVector.x, this.personHeight, newCenterVector.y];
    // 設置當前 center 的大小
    this.g3d.setCenter(newCenterPosition); 
}

通過上述代碼就實現了場景中的視角旋轉,並且可以通過修改 rotateValue 的值控制旋轉的速度。

電梯動畫代碼的實現分析

場景中電梯是一個 obj 模型,3D 模型是由最基礎的三角形面拼接合成,例如 1 個矩形可以由 2 個三角形構成,1 個立方體由 6 個面即 12 個三角形構成,以此類推更復雜的模型可以由許多的小三角形組合合成。因此 3D 模型定義即爲對構造模型的所有三角形的描述,而每個三角形由三個頂點 vertex 構成,每個頂點 vertex 由 x, y, z 三維空間座標決定,HT 中使用 vs 數組記錄構成三角面的所有頂點座標,所以如果想要讓電梯運行起來,只需要把所有的頂點座標往電梯運行的方向進行平移,以下爲部分關鍵僞代碼:

// vs 指的是構成電梯模型所有的三角面頂點座標數組
// 由於場景中電梯的運行方向爲往對角線右上方運動,所以只需要修改 x 軸以及 y 軸座標值
// xStep yStep 爲每次電梯運動的距離
setInterval(() => {
    // i+3 是因爲 vs 數組的順序爲 x, y, z 軸 所以每次 i 偏移三個單位大小
    for(let i = 0, l = vs.length; i < l; i = i + 3) {
        // 該頂點座標下一個 x 軸座標的值
        let nextX = vs[i] - xStep;
        // 該頂點座標下一個 y 軸座標的值
        let nextY = vs[i + 1] + yStep;
        vs[i] = nextX < -0.5 ? 0.5 - (Math.abs(nextX) - 0.5) : nextX;
        vs[i + 1] = nextY > 0.5 ? -0.5 + (Math.abs(nextY) - 0.5) : nextY;
    }
}, 200);

電梯運動動畫如下圖所示:

圖片描述

監控功能展示及介紹

視頻監控

當點擊場景中的攝像頭之後右側頂部會顯示出當前攝像頭的監控畫面,以下爲實現效果圖:

圖片描述

煙霧報警監控

煙霧報警會根據後臺實時傳遞過來的狀態值來變換當前煙霧報警模型的顏色,紅色爲報警狀態,以下爲實現效果圖:

圖片描述

電視列車到站時間監控

日常地鐵站中會有專門的電視來展示下一班地鐵到站的時間表,該系統中也模擬該效果,不過該系統暫時做了電視的模型,時間暫無對接,以下爲效果圖:

圖片描述

場景監控交互

3D 場景中交互是比較簡單的,主要是點擊攝像頭展示 2D 監控面板,在 2D 界面中主要是切換三種交互模式,三種交互模式爲互斥的關係,以下是 3D 交互註冊事件代碼:

g3d.mi((e) => {
    let {g2d, dm2d} = this;
    // 爲點擊類型
    if(e.kind === 'clickData') {
        // data 爲當前點擊的圖元
        let data = e.data;
        // 當前圖元的 shape3d 類型
        let shape3d = data.s('shape3d');
        // 判斷當前 shape3d 類型是否爲攝像頭
        if(shape3d && shape3d.indexOf('攝像頭') > 0) {
            let cameraPanel = dm2d.getDataByTag('cameraPanel');
            // toggle 切換攝像頭 2d 面板
            g2d.isVisible(cameraPanel) ? cameraPanel.s('2d.visible', false) : cameraPanel.s('2d.visible', true);
        }
    }
    // 爲點擊 3d 場景背景類型
    if(e.kind === 'clickBackground') {
        let cameraPanel = dm2d.getDataByTag('cameraPanel');
        // 隱藏攝像頭 2d 面板
        g2d.isVisible(cameraPanel) && cameraPanel.s('2d.visible', false);
    }
});

總結

工業互聯網將人,數據和機器連接起來,地鐵站 3D 可視化系統則是一個很好的展現,HT 的輕量化,數據的可視化,機器的可視化,資產的管理化幫助我們更好的監控。而物聯網將通過各種信息傳感設備,實時採集任何需要監控、連接、互動的物體或過程等各種需要的信息,通過與 HT 的結合更好的展現出可視化的優勢,當然地鐵站還可以與 VR 進行結合,在各地科技展會中我們可以見到各種 VR 場景操作,HT 中也可以結合 VR 設備進行操作,可以戴上設備在地鐵站中漫遊,讓人有身臨其境的感覺,由於場景本身的輕量化,所以 VR 場景下的流暢性也是十分的高,讓用戶不會有頭暈的感覺。當然系統本身也可以在移動端運行,以下爲移動端運行截圖:

圖片描述

程序運行截圖:

圖片描述
圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章