前言
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,相應偽代碼如下:
1 // graph3dView 為 HT 中的三維場景視圖容器 2 // vr 獲取掛載在 graph3dView 上的 vr 對象 3 var vr = graph3dView.vr; 4 vr.mp(function(e) { 5 // property 對應的 vr 事件類型,detail 此時事件的狀態 6 var property = e.property; 7 var detail = e.newValue; 8 // present 代表此時進入或者離開 VR 場景 9 if (property === 'present') { 10 // 此時 detail 為 true 表示進入 vr,false 表示離開 vr 11 if (detail) { 12 // 執行顯示 vr 場景中需要顯示的節點操作 13 } else { 14 // 執行隱藏 vr 場景中需要隱藏的節點操作 15 } 16 } 17 });
上面 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 的配置項,如下:
1 var vr_config = { 2 measureOflength: 0.01, 3 }
上面 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 對象內,如下:
1 var vr_config = { 2 measureOflength: 0.01, 3 vrEnable: true, // 代表開啟 VR 4 }
在該展示的系統中有直接在 VR 中切換場景的功能,由於每個場景的 vr_config 中的配置項值可能會有差別,例如第一個場景的 measureOflength 比例尺的大小為 0.01,可能第二個場景的比例尺大小 measureOflength 就變成了 0.02,所以 VR 插件提供一個銷毀的功能,用來銷毀上一個場景的資源,銷毀場景的資源包括清空上一個場景的所有節點,所以在加載新的場景時,不需要再執行清空場景節點的操作,即不需要執行 dataModel.clear(),因為 VR 提供的銷毀功能已經都清空了,手柄和射線都是場景中的一個 Data 節點,所以在新的場景不需要額外的清除手柄和射線這兩個節點,故插件幫你管理場景的節點。在調用銷毀功能之后,可以調用 graph3dView 的序列化函數 graph3dView.deserialize('場景資源json地址') 來序列化新的場景 json 文件,在序列化完成的回調函數中,可以根據新的場景修改此時 vr_config 的值,然后再次調用 graph3dView.initVRForScene() 來再次初始化 VR 場景。相關的步驟偽代碼如下:
1 // window.GVR 是在調用 graph3dView.initVRForScene() 之后初始化的一個全局 VR 插件變量 用於用戶獲取插件對象 2 window.GVR.destory(); 3 // 執行新的場景序列化操作 4 graph3dView.deserialize('場景資源json地址', 5 function(json, dm, g3d, datas) { 6 // 修改新的場景比例尺為 0.02 7 window.vr_config.measureOflength = 0.02; 8 // 修改新的 VR 場景初始化視角 9 window.vr_config.vrEye = ht.Default.clone(g3d.getEye());; 10 // 再次初始化 VR 場景 11 graph3dView.initVRForScene() 12 });
當然 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 偽代碼:
1 // 記錄拆解順序 2 const queue = []; 3 // 記錄還原順序 4 const stack = []; 5 // each 循環中用來記錄拆解隊列 queue 順序 6 dataModel.each((node) = >{ 7 const displayName = node.getDisplayName(); 8 if (displayName) { 9 const[index, distancePer, during] = displayName.split('-'); 10 if (index !== void 0) { 11 if (queue[index]) { 12 if (queue[index] instanceof Array) { 13 queue[index].push(node); 14 } else { 15 const tempNode = queue[index]; 16 queue[index] = [tempNode, node]; 17 } 18 } else { 19 queue[index] = node; 20 } 21 } 22 } 23 });
相關邏輯如下流程圖:
通過上面的約定,設計師可以使用可視化編輯器來配置不同零件的移動規則,大大提高了動畫的制作效率。
代碼分析
該部分主要對拆解還原動畫的代碼進行分析,主要采用向量和部分三角函數的概念來計算不同零件在三維空間的位置,初始的時候需要記錄下每個零件在前面所有組合移動之后的初始移動位置向量,以及零件沒有組合移動之前的初始位置向量,獲取這兩個位置向量目的是一是為了零件拆解在前面所說組合之后移動,和零件在拆解之后恢復到一整個設備形態的初始位置,兩個位置向量都有重要的作用,以下為相關偽代碼:
1 // Vector3 為 HT 封裝的三維向量 2 const Vector3 = ht.Math.Vector3; 3 // 記錄第一個重要位置向量 4 node.a('relativeP3Vec', new Vector3(node.p3())); 5 // node 當前零件節點 6 // moveQueue 為移動順序在 node 之前的,並且為 node 節點的祖先節點 7 for (let i = 0, l = moveQueue.length; i < l; i++) { 8 const moveNode = moveQueue[i], 9 parentMoveNode = moveNode.getParent(); 10 if (parentMoveNode) { 11 const[, distancePer] = moveNode.getDisplayName().split('-'); 12 moveNode.a('defP3', moveNode.p3()) moveNode.p3(new Vector3().lerpVectors(new Vector3(moveNode.p3()), new Vector3(parentMoveNode.p3()), distancePer).toArray()); 13 } 14 } 15 // 記錄組合節點移動之后的第二個重要相對位置向量 16 node.a('relativeP3Vec', new Vector3(node.p3())); 17 // 逆序還原組合的父節點位置 18 for (let i = moveQueue.length - 1; i >= 0; i--) { 19 const moveNode = moveQueue[i]; 20 moveNode.p3(moveNode.a('defP3')); 21 moveNode.a('defP3', undefined); 22 }
由於在場景拆解過程中需要設置設備零件節點不可選擇,所以需要記錄下不可選擇之前的零件是否可選擇狀態,用來恢復節點初始狀態,相關偽代碼如下:
1 dm3d.each((node) = >{ 2 node.a('defSelectable', node.s('3d.selectable')); 3 });
文中所示的線框效果為 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 帶來的應用遠遠不止仿真,模擬等體驗,更多帶來的是能為人們提供真實的實際作用,而不是噱頭。
程序手機端運行截圖: