基於 HTML5 WebGL + WebVR 的 3D 虛實現實視覺化培訓系統

圖撲軟體發表於2020-04-06

 

前言

2019 年 VR, AR, XR, 5G, 工業網際網路等名詞頻繁出現在我們的視野中,資訊的分享與虛實的結合已經成為大勢所趨,5G 是新一代資訊通訊技術升級的重要方向,工業網際網路是製造業轉型升級的發展趨勢。本文所講的 VR 是機械製造業與裝置的又一次交流,當技術新星遇上製造潮流,無疑將成為製造業,工控業等行業數字化轉型的重要驅動力。“5G + VR + 工業網際網路”必將成為新的一年不變的話題,如何將當前工業中遇到的問題通過虛擬現實結合起來,讓我們可以更近的去交流,去感受技術帶給我們的變化。在今年蘋果的釋出會上,相信大家都知道蘋果的 5G 手機沒有問世,說明 5G 的應用和發展還處在快速發展的階段,但是手機結合 AR 功能的 APP 已經早就問世,5G 的速度加上 AR, VR 的身臨其境,讓我們感受到的不僅僅是技術的革新,更是讓我們感受到技術在不同領域的實際應用場景,我相信 2020 年新的一年必定是 “5G + VR + 工業網際網路” 應用的又一個新的開始,本文接下來所講的就是 HT for Web 結合 WebVR 開發的具體應用案例。

系統預覽

預覽地址:基於 HTML5 WebGL 與 WebVR 3D 虛實現實的視覺化培訓系統 http://www.hightopo.com/demo/vr-training/

VR 拆解還原

VR 操作

VR 場景切換

PC 端拆解還原

PC 端考試

系統介紹

該系統共分為三個實際應用層面:

  • 三維培訓: 使用者通過 mb 端手指觸控或者 pc 端滑鼠拖拽可以將裝置拆解開來,之後可以通過一鍵還原來將裝置還原到最初的狀態,或者可以通過拆解 or 還原按鈕檢視裝置自動拆解的過程以及拆解之後自動還原的過程。
  • 考試系統: 這部分是考驗你對裝置拆解的熟悉程度,在第一步的三維培訓之後,可以在該系統中考核你對拆解過程的瞭解。
  • VR 模式: 該部分便是三維場景結合 WebVR 的具體實現應用,在進入 VR 之後可以通過操作 VR 手柄,進行裝置的拆解還原。

文章主要講解第三部分的 VR 模式,讓我們瞭解如何結合 HT 來搭建 VR 場景。下面描述了 VR 中的主要操作,沒有進入 VR 的時候不會出現如下所說的六個按鈕操作,在點選進入 WebVR 時,系統自動顯示出 VR 場景裡的六個操作按鈕,反之退出 VR 時,系統也會自動隱藏三維中的六個操作按鈕,VR 中的主要操作如下:

  • 裝置切換: 顧名思義,可以通過手柄射線對準場景中左側列表,按動板機進行場景裝置切換。
  • 操作切換: VR 中對裝置有如下兩種操作,可以通過右下角的模式按鈕點選切換。
  • 平移模式: 該模式下,使用者可以對準裝置並且按動板機將裝置從一個位置移動到另一個位置,並且可以通過觸控觸控板來拉近和拉遠裝置零件。
  • 抓取模式: 該模式下,使用者可以對準裝置並且按動板機將裝置抓取過來,抓取過來之後,可以通過觸控觸控板來旋轉以及放大或者縮小零件。
  • 一鍵還原: 將裝置各部分零件還原到最初始的位置。
  • 拆解動畫: 將裝置的各部分零件通過之前預定好的位置按步驟一步一步拆解開來。
  • 還原動畫: 該操作可以理解為拆解動畫的倒放,即將拆解的過程逆序還原。
  • 線框切換: HT 支援將裝置節點的三角面表示出來,可以具體的看到該裝置的線框輪廓。

系統開發

三維場景
HT 支援 obj 模型的匯入,VR 場景所出現的裝置零件均為 obj 模型,由於需要在之後進行裝置的拆解,所以建模的時候需要分別對裝置的各部分零件進行建模,而不是對裝置整體進行建模,如果對裝置整體建模那麼在 HT 的場景中就是一個 Data 節點,從而不能對零件進行拆解,如果拆解開來,那麼在 HT 中可以載入多個 obj 則就有多個 Data 節點,有多個零件的 Data 節點之後就可以對裝置零件進行移動或者其它旋轉操作,具體的 Data 在 HT 的含義可以參考 HT for Web 資料模型手冊

如下為匯入場景中的 obj 模型:

從上圖可以看出我們匯入 obj 之後零件之間是分散的,所以需要對零件的初始位置進行調整,從而調整出一個由許多零件構成的完整裝置,當然調整不可能通過程式碼來調整,對應的有三維編輯器可以調整,進行拖拖拽拽將不同零件拼湊起來,如下為組合之後的裝置整體:

VR 搭建
VR 場景的搭建是在第一步的基礎上進行搭建,上面所說的只在 VR 場景中顯示的按鈕也是在場景中進行搭建,在正常的場景時候我們可以隱藏掉對應的節點,node.s('3d.visible', false) 上面的程式碼就是 HT 中在三維下面隱藏三維節點的程式碼,因為進入 VR 和離開 VR 的時候,HT 內部會派發出對應的狀態告訴使用者此時已經進入 VR 或者此時已經離開 VR,相應虛擬碼如下:

 

 

 

// graph3dView 為 HT 中的三維場景檢視容器
// vr 獲取掛載在 graph3dView 上的 vr 物件
var vr = graph3dView.vr;
vr.mp(function(e) {
    // property 對應的 vr 事件型別,detail 此時事件的狀態
    var property = e.property;
    var detail = e.newValue;
    // present 代表此時進入或者離開 VR 場景
    if (property === 'present') {
        // 此時 detail 為 true 表示進入 vr,false 表示離開 vr
        if (detail) {
            // 執行顯示 vr 場景中需要顯示的節點操作
        } else {
            // 執行隱藏 vr 場景中需要隱藏的節點操作
        }
    }
});

 

上面 property 在 HT 總共會派發出以下幾種型別,主要是包括 VR 的狀態和手柄的操作型別:

  • enable: vr 的 enable 資訊發生變化
  • present: vr 的 present 資訊發生變化,表明進出 vr 世界
  • gamepad.pose: 手柄位置或旋轉發生變動,引數 id,position,rotation
  • gamepad.axes: 手柄中間的轉盤觸控點位變動,引數 id,axes;其中 axes 格式形如:[ 0.2, 0.7 ],分辨表示橫縱百分比
  • gamepad.button.thumbpad: thumbpad 按鍵被按下,引數 id,state,其中 state 包含 down 跟 up 兩種
  • gamepad.button.trigger: trigger 按鍵被按下,引數 id,state,其中 state 包含 down 跟 up 兩種
  • gamepad.button.grips: grips 按鍵被按下,引數 id,state,其中 state 包含 down 跟 up 兩種
  • gamepad.button.menu: menu 按鍵被按下,引數 id,state,其中 state 包含 down 跟 up 兩種

VR 中有一個關鍵的配置就是比例尺,因為 VR 裡面的單位是和現實中的長度單位是一致的,我們戴著頭盔往前走 1m 那麼對應在 HT 三維場景中需要往前走多遠這需要一個對應關係,HT 提供的 VR 外掛中會提供一個 measureOflength 的配置項,如下:

var vr_config = {
   measureOflength: 0.01,
}

上面 0.01 代表的意思就是 HT 場景中的單位長度 1 代表現實場景的 0.01 米,所以如果此時現實場景你戴著頭盔往前移動 1m,那麼 HT 中對應的視角會往前移動 100 個單位,所以如果需要搭建 VR 場景要注意場景的模型建模比例和現實世界是相差多少,按照統一的比例來建模,不然在 VR 場景中會出現裝置大小不一的問題,導致出現錯覺,如下對比圖,左側是 0.01 的比例,射線的小點很小,右側是是 0.001 的比例導致射線的小點變大。

HT 中已經對瀏覽器提供的 WebVR 相關介面的 API 進行了封裝,包括獲取裝置 navigator.getVRDisplays() 這是進入 VR 世界的第一步,如果此時執行此程式碼返回的結果為空代表獲取 VR 裝置失敗,那麼之後更不用說了,以及獲取手柄資訊 navigator.getGamepads(),使用者可以通過在瀏覽器控制檯敲入上面兩行程式碼,檢視瀏覽器是否已經獲取到了 VR 裝置資訊和 VR 手柄資訊,如果返回為空則說明獲取失敗。HT 只要通過執行 graph3dView.vr.enable = true 就可以開啟 VR,當然使用者不用執行該程式碼,HT 提供的 VR 外掛也會提供對應的配置項 vrEnable: true 來代表開啟 VR,對應的配置也掛在在上面的 vr_config 物件內,如下:

var vr_config = {
    measureOflength: 0.01,
    vrEnable: true, // 代表開啟 VR
}

在該展示的系統中有直接在 VR 中切換場景的功能,由於每個場景的 vr_config 中的配置項值可能會有差別,例如第一個場景的 measureOflength 比例尺的大小為 0.01,可能第二個場景的比例尺大小 measureOflength 就變成了 0.02,所以 VR 外掛提供一個銷燬的功能,用來銷燬上一個場景的資源,銷燬場景的資源包括清空上一個場景的所有節點,所以在載入新的場景時,不需要再執行清空場景節點的操作,即不需要執行 dataModel.clear(),因為 VR 提供的銷燬功能已經都清空了,手柄和射線都是場景中的一個 Data 節點,所以在新的場景不需要額外的清除手柄和射線這兩個節點,故外掛幫你管理場景的節點。在呼叫銷燬功能之後,可以呼叫 graph3dView 的序列化函式 graph3dView.deserialize('場景資源json地址') 來序列化新的場景 json 檔案,在序列化完成的回撥函式中,可以根據新的場景修改此時 vr_config 的值,然後再次呼叫 graph3dView.initVRForScene() 來再次初始化 VR 場景。相關的步驟虛擬碼如下:

// window.GVR 是在呼叫 graph3dView.initVRForScene() 之後初始化的一個全域性 VR 外掛變數 用於使用者獲取外掛物件
window.GVR.destory();
// 執行新的場景序列化操作
graph3dView.deserialize('場景資源json地址',
function(json, dm, g3d, datas) {
    // 修改新的場景比例尺為 0.02
    window.vr_config.measureOflength = 0.02;
    // 修改新的 VR 場景初始化視角
    window.vr_config.vrEye = ht.Default.clone(g3d.getEye());;
    // 再次初始化 VR 場景
    graph3dView.initVRForScene()
});

當然 HT 提供的 VR 外掛還有很多的配置項,方便使用者更好的調整 VR 場景,包括刷地形,場景移動方式,場景操作方式都可以通過配置進行配置,利用 HT 進行 VR 搭建主要流程如下流程圖所示:

通過上面的流程圖,我們大體可以瞭解配合 HT 提供的 VR 外掛如何進行快速的搭建 VR 場景。

目前谷歌瀏覽器和火狐瀏覽器都很友好的支援 VR,可以通過火狐官網提供的 WebVR Demo 線上感受下官方提供的 VR 場景。

拆解規則
從文章前面的部分效果圖可以看到我們每個場景的裝置都有拆解,並且每個裝置的零件數量,零件位置,零件拆解的方向,偏移的長短都是不一致的,所以不可能通過程式碼來將上面的偏移長短,偏移方向寫死,需要制定一套拆解規則來幫助我們可以更方便製作每個場景的拆解動畫,這樣只需要設計師根據與程式約定好的拆解規則進行配置就可以配置出不同場景不同裝置的拆解動畫。該系統的拆解分為兩種情況:

  • 單體移動: 單個裝置零件沿著父節點位置和該節點位置的連線線方向移動
  • 組合移動: 多個裝置零件的組合沿著某個方向移動,組合移動之後,裝置零件可以在組合移動之後的位置進行再沿著某個方向進行移動,可以無限進行巢狀,即組合之後還可以組合移動,或者單體移動

單體移動示意圖如下:

組合移動示意圖如下:

在 HT 中可以通過 data.setDisplayName('名稱') 來給節點設定名稱,這裡約定通過不同裝置的名稱,來獲取到不同裝置的偏移資訊,例如 data.setDisplayName('1-0.5-1000') 該名稱就是和設計師約定好的配置規則,1 代表拆解步驟的第一步執行,當然場景中可以有多個 1,即第一步同時拆解這些零件 0.5 代表朝著父節點的方向偏移自己位置和父節點位置連線線長度的 50%1000 代表偏移的過程持續 1000 毫秒,當然之後可以約定旋轉以及旋轉的角度等資訊。設計師知道這些配置規則之後便可以通過視覺化編輯器進行不同零件的配置,這樣程式方面只需要寫一套通用的邏輯就可以對不同的裝置進行拆解和還原。

系統中維護了一個佇列和一個佇列用來記錄拆解順序用來記錄還原順序。拆解的過程通過配置的序號,按順序推進佇列,採用佇列的資料結構便是因為佇列先進先出的特點,第一個壓入佇列的零件則第一個執行,最後壓入佇列的零件最後一個執行拆解順序。拆解出佇列的零件則同時壓入棧,採用記錄還原順序是因為先進後出的特點,即第一個執行完拆解的零件,在還原的時候卻是最後一個執行還原的動作。所以上述採用的不同資料結構便是為了更好的記錄資料。以下為相關 js 虛擬碼:

// 記錄拆解順序
const queue = [];
// 記錄還原順序
const stack = [];
// each 迴圈中用來記錄拆解佇列 queue 順序
dataModel.each((node) = >{
    const displayName = node.getDisplayName();
    if (displayName) {
        const[index, distancePer, during] = displayName.split('-');
        if (index !== void 0) {
            if (queue[index]) {
                if (queue[index] instanceof Array) {
                    queue[index].push(node);
                } else {
                    const tempNode = queue[index];
                    queue[index] = [tempNode, node];
                }
            } else {
                queue[index] = node;
            }
        }
    }
});

相關邏輯如下流程圖:

通過上面的約定,設計師可以使用視覺化編輯器來配置不同零件的移動規則,大大提高了動畫的製作效率。

程式碼分析
該部分主要對拆解還原動畫的程式碼進行分析,主要採用向量和部分三角函式的概念來計算不同零件在三維空間的位置,初始的時候需要記錄下每個零件在前面所有組合移動之後的初始移動位置向量,以及零件沒有組合移動之前的初始位置向量,獲取這兩個位置向量目的是一是為了零件拆解在前面所說組合之後移動,和零件在拆解之後恢復到一整個裝置形態的初始位置,兩個位置向量都有重要的作用,以下為相關虛擬碼:

// Vector3 為 HT 封裝的三維向量
const Vector3 = ht.Math.Vector3;
// 記錄第一個重要位置向量
node.a('relativeP3Vec', new Vector3(node.p3()));
// node 當前零件節點
// moveQueue 為移動順序在 node 之前的,並且為 node 節點的祖先節點
for (let i = 0, l = moveQueue.length; i < l; i++) {
    const moveNode = moveQueue[i],
    parentMoveNode = moveNode.getParent();
    if (parentMoveNode) {
        const[, distancePer] = moveNode.getDisplayName().split('-');
        moveNode.a('defP3', moveNode.p3()) moveNode.p3(new Vector3().lerpVectors(new Vector3(moveNode.p3()), new Vector3(parentMoveNode.p3()), distancePer).toArray());
    }
}
// 記錄組合節點移動之後的第二個重要相對位置向量
node.a('relativeP3Vec', new Vector3(node.p3()));
// 逆序還原組合的父節點位置
for (let i = moveQueue.length - 1; i >= 0; i--) {
    const moveNode = moveQueue[i];
    moveNode.p3(moveNode.a('defP3'));
    moveNode.a('defP3', undefined);
}

由於在場景拆解過程中需要設定裝置零件節點不可選擇,所以需要記錄下不可選擇之前的零件是否可選擇狀態,用來恢復節點初始狀態,相關虛擬碼如下:

dm3d.each((node) = >{
    node.a('defSelectable', node.s('3d.selectable'));
});

文中所示的線框效果為 HT 核心包支援的線框模式,可以通過如下程式碼進行配置:

dm3d.each((data) = >{
    if (data.s('shape3d') && data.s('shape3d').startsWith('models/')) {
        data.s({
            'shape3d.transparent': true,
            'shape3d.opacity': 0, // 目的為隱藏原本的模型
            'wf.geometry': true, // 開啟線框模式
            'wf.combineTriangle': 2, // 線框三角面合併型別
            'wf.color': 'rgba(96,172,252,0.3)' // 線框顏色
        });
    }
});

上述 wf.combineTriangle 主要包括

  • false,0: 不合並三角形
  • true,1: 合併相鄰三角為四邊面,原來的效果
  • 2: 融合所有聯通的共面三角面
  • 3: 根據法線資訊融合所有平滑三角面

VR 軟體以及硬體安裝

本系統採用的 VR 硬體裝置為 HTC VIVE 接下來講的是安裝 HTC VIVE 的過程和步驟。

第一步:撮合 HTC VIVE 和電腦主機

HTC 官網找到連線指南,然後按照步驟安裝即可,我們只需看以下截圖部分的目錄即可。

第二步:下載軟體

Steam 官網下載 Steam,下載完 Steam 可以在 Steam 中下載 Stream VR。

第三步:開啟 Stream VR 檢查裝置狀態

開啟 Stream VR,會出現以下畫面,這是用來表示 HTC VIVE 頭顯的工作狀態的,通過圖示我們即可檢視頭顯、手柄控制器和定位器等配件的工作情況。

第四步:選擇房間設定模式

如果您的房間位置比較大可以選擇第一項,我選擇的模式為第二項,站立模式。建議選擇一種房間規模,可以完整的進行設定。

第五步:將頭盔、兩個手柄控制器放置在兩個定位器可視範圍內,建立定位

第六步:校準頭盔中心點

該步為設定頭盔預設的朝向。

第七步:定位地面

將兩個手柄控制器放置在定位器可視範圍內,然後點選電腦螢幕上的按鈕“校準地面”,等待系統校準

第八步:進入 Steam VR 自帶房間進行測試

設定完畢之後可以進入 Steam VR 自帶的房間進行體驗。

總結
當人們談起 5G 時代的新應用,VR、AR 總是一大熱門話題。4G 時代行動網路已經足以承載起高清視訊,那麼 5G 時代理所當然就能傳輸資料量更大的沉浸式 VR、AR 影像。因此,不少人將 5G 視為 VR、AR 崛起的踏板,隨時隨地身臨天涯海角,似乎並非是遙不可及的夢。當前 4G 網路應用在 VR/AR 上會帶來大約 70ms 的時延,這個時延會導致體驗者存在眩暈感,而 5G 資料傳輸的延遲可達到毫秒級,可以有效解決資料時延帶來的眩暈感,有助於 VR/AR 的大規模應用。目前隨著 5G 網路的逐漸普及,VR/AR 產業正逐步走向復甦,市場熱情在逐漸升溫,虛擬現實遊戲、虛擬現實現場直播等都是 5G 在 VR/AR 上的具體應用。在科技進步的今天,安全也是一個重要的話題,VR 結合模擬的應用也是大勢所趨,模擬可以讓使用者真實切身感受,例如消防預警管道預警,可以讓使用者在 VR 世界中體驗消防滅火等消防員的操作,讓使用者沉浸在 VR 世界中感受到火災來臨時怎麼進行實際操作。所以 VR 帶來的應用遠遠不止模擬,模擬等體驗,更多帶來的是能為人們提供真實的實際作用,而不是噱頭。

程式手機端執行截圖:

 

 

 

 

相關文章