three.js實現數字孿生3D倉庫一期(開源)

杨元超發表於2024-04-22

大家好,本文使用three.js實現了3D倉庫一期專案,給出了程式碼,分析了關鍵點,感謝大家~

關鍵詞:數字孿生、three.js、Web3D、WebGL、智慧倉庫、開源

程式碼:Github

我正在承接Web3D數字孿生專案,具體介紹可看承接各種Web3D業務

加QQ群交流:106047770

目錄
  • 需求描述
  • 建模
  • Instanced Draw
  • Label
  • 實現人物
  • 彈出抽屜
  • UI
  • 切換場景
  • 適配
    • 橫屏
    • 第三人稱在移動端顯示虛擬搖桿
    • IOS系統適配
  • 參考資料

需求描述

客戶想要把兩個倉庫3D化,方便視覺化地檢視倉庫的內容。未來可以進行搜尋之類的操作

需要在PC端、平板、安卓手機、蘋果手機的瀏覽器上執行

實際場景如下圖所示:
倉庫1有兩排櫃子:
image
image

第一排的櫃子都是一樣的,其中每個櫃子有80個一樣的抽屜:
image

第二排的每個櫃子也是一樣的,並且不需要顯示櫃子中的畫

倉庫2有一排櫃子,每個櫃子都是一樣的:
image

一期的需求如下:

  • 1比1地還原場景
  • 點選櫃子的抽屜,可彈出抽屜,並顯示繫結的資料

最終實現效果演示:

image

下面開始實現各個關鍵點,給出實現的思路:

建模

我們首先拿著捲尺、鐳射測距儀,到現場去測量,得到實際場景中的櫃子大小、抽屜大小、間隙、倉庫大小、櫃子在倉庫的位置等資料,我們要根據這些資料來1比1建模

現在對倉庫1建模,首先要建立倉庫:
透過拼接多個立方體(BoxGeometry)來建立牆、門;
透過PlaneGeometry來建立地面,設定它的材質的map來貼上瓷磚。值得注意的是瓷磚的大小要與實際場景一致,這是透過設定map的repeat來實現。瓷磚紋理可以從這裡獲得

建模後的倉庫如下圖所示:
image

倉庫由牆、地面、門組成

接著,我們對第一排的一個櫃子建模:
我們透過CSG來構造櫃體;
因為客戶對模型要求較高,所以不用CSG來構造抽屜,而是由美術來建模抽屜。值得注意的是要烘焙抽屜拉口中的陰影到貼圖中。另外,本來還想把抽屜之間的AO(抽屜之間是黑色的)也烘焙到ao貼圖或者normal貼圖中,但美術始終沒有做出來。另外,進行了簡化處理,如抽屜上的兩個標籤就不需要顯示;
透過拼接圓柱體、立方體來建立櫃子把手、櫃子軌道

建模後的櫃子如下圖所示,其中抽屜是克隆的:
image

第一排的其餘櫃子都是一樣的,只是Transform不同

最後,我們對第二排的櫃子建模:
同樣透過CSG來構造櫃體;
使用GridHelper來構造網格;
同樣透過拼接圓柱體、立方體來建立櫃子把手、櫃子軌道

建模後的櫃子如下圖所示:
image

第二排的其餘櫃子都是一樣的,只是Transform不同

最終,建模後的倉庫1如下圖所示:
image

倉庫2的建模也是類似的,建模後如下圖所示:
image

Instanced Draw

因為倉庫1第一排櫃子的抽屜數量較多(有1千多個),所以會造成drawcall過多(每個抽屜都是一個drawcall),這在移動端會有明顯的效能問題

因為有抽屜動畫(彈出抽屜),不能將其merge,所以使用Instanced Draw來批次繪製抽屜

值得注意的是抽屜可能是多材質的Object3D(雖然此處是單材質的Mesh),所以要將其中的每個Mesh拆分到一組Instanced Draw中。舉例來說,如果抽屜有個3個材質(即3個子Mesh),則需要建立3個InstancedMesh,然後將所有抽屜的第一個材質的Mesh對應到第一個InstancedMesh中,其餘的以此類推

Label

每個櫃子上面需要顯示編號,如下圖所示:
image

它是billboard,透過Sprite來實現

實現人物

可以切換軌道相機和第三人稱相機,後者會顯示人物。如下圖所示:

image

第三人稱相機的實現請參考Threejs 從零開始實現第三人稱漫遊

人物我們是在這裡下載的,帶骨骼動畫的FBX

使用AnimationMixer來播放人物的骨骼動畫,需要實現動畫的管理

人物需要與倉庫、櫃子進行碰撞檢測。我們使用Octree來構造場景的八叉樹(只構造一次,構造牆、門、地面、櫃體的八叉樹,為了效能考慮這裡排除掉抽屜),人物則用Capsule作為包圍盒,透過檢測Capsule和八叉樹的相交來處理碰撞

彈出抽屜

雙擊倉庫1的第一排的櫃子,可以進入單個櫃子的操作模式;然後點選任意的抽屜,可彈出該抽屜,顯示繫結的資料。如下圖所示:

image

實現點選的原理是:
我們將第一排的櫃子的layer設定為可點選的,並透過Raycaster與該layer進行ray picking(這是最佳化,以免與其餘的物體進行ray picking),並區分單擊和雙擊

進入單個櫃子的操作模式後,我們使用TrackControls、OrthographicCamera來正交地檢視單個櫃子,並隱藏其餘的櫃子

透過使用Tween來建立彈出、彈回抽屜的關鍵幀動畫。點選單個抽屜時,播放彈出動畫。彈出後顯示繫結的資料(此處只顯示了櫃子編號和抽屜編號,以後可透過後端請求來獲得相關的資料)

UI

UI框架使用React+Redux

普通UI使用Antd

大屏UI使用DataV-React+ECharts

大屏UI如下圖所示:
image

“3D倉庫-倉庫1”這個Header、左側的餅圖是大屏UI

切換場景

能夠切換倉庫1、倉庫2場景,如下圖所示:
image

切換時,需要先深度刪除當前場景的所有執行時資料(包括Mesh、Material、Geometry、Texture等),然後建立下一個場景的執行時資料

值得注意的是這兩個場景的資源只需載入一次

適配

對於移動端需要進行適配

橫屏

移動端需要橫屏顯示。我們檢測如果是豎屏,則提示使用者橫屏

在微信中開啟橫屏的方法:
1、我->通用->開啟橫屏模式
2、開啟手機的自動旋轉
3、將手機橫屏

第三人稱在移動端顯示虛擬搖桿

如下圖所示:
image

透過nipplejs來實現虛擬搖桿

第三人稱相機在移動端的使用說明:
將左手拇指放到螢幕左方透明的操作杆上,右手拇指放到螢幕右方的任意位置。其中,左手控制人物移動,右手旋轉螢幕視角

IOS系統適配

IOS系統中值得注意的地方:

  • 不應該動態插入/刪除dom
  • 構造octree太慢
  • 紋理大小不能太大(不能>=4096*4096)

參考資料

Web3D數字孿生有什麼參考資料(最好有原始碼)?

如何用webgl(three.js)搭建一個3D庫房-第一課

相關文章