three.js實現(xiàn)數(shù)字孿生3D倉庫一期(開源)
大家好,本文使用three.js實現(xiàn)了3D倉庫一期項目,給出了代碼,分析了關(guān)鍵點,感謝大家~
關(guān)鍵詞:數(shù)字孿生、three.js、Web3D、WebGL、智慧倉庫、開源
代碼:Github
我正在承接Web3D數(shù)字孿生項目,具體介紹可看承接各種Web3D業(yè)務(wù)
加QQ群交流:106047770
需求描述
客戶想要把兩個倉庫3D化,方便可視化地查看倉庫的內(nèi)容。未來可以進(jìn)行搜索之類的操作
需要在PC端、平板、安卓手機(jī)、蘋果手機(jī)的瀏覽器上運行
實際場景如下圖所示:
倉庫1有兩排柜子:


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

第二排的每個柜子也是一樣的,并且不需要顯示柜子中的畫
倉庫2有一排柜子,每個柜子都是一樣的:

一期的需求如下:
- 1比1地還原場景
- 點擊柜子的抽屜,可彈出抽屜,并顯示綁定的數(shù)據(jù)
最終實現(xiàn)效果演示:

下面開始實現(xiàn)各個關(guān)鍵點,給出實現(xiàn)的思路:
建模
我們首先拿著卷尺、激光測距儀,到現(xiàn)場去測量,得到實際場景中的柜子大小、抽屜大小、間隙、倉庫大小、柜子在倉庫的位置等數(shù)據(jù),我們要根據(jù)這些數(shù)據(jù)來1比1建模
現(xiàn)在對倉庫1建模,首先要創(chuàng)建倉庫:
通過拼接多個立方體(BoxGeometry)來創(chuàng)建墻、門;
通過PlaneGeometry來創(chuàng)建地面,設(shè)定它的材質(zhì)的map來貼上瓷磚。值得注意的是瓷磚的大小要與實際場景一致,這是通過設(shè)置map的repeat來實現(xiàn)。瓷磚紋理可以從這里獲得
建模后的倉庫如下圖所示:

倉庫由墻、地面、門組成
接著,我們對第一排的一個柜子建模:
我們通過CSG來構(gòu)造柜體;
因為客戶對模型要求較高,所以不用CSG來構(gòu)造抽屜,而是由美術(shù)來建模抽屜。值得注意的是要烘焙抽屜拉口中的陰影到貼圖中。另外,本來還想把抽屜之間的AO(抽屜之間是黑色的)也烘焙到ao貼圖或者normal貼圖中,但美術(shù)始終沒有做出來。另外,進(jìn)行了簡化處理,如抽屜上的兩個標(biāo)簽就不需要顯示;
通過拼接圓柱體、立方體來創(chuàng)建柜子把手、柜子軌道
建模后的柜子如下圖所示,其中抽屜是克隆的:

第一排的其余柜子都是一樣的,只是Transform不同
最后,我們對第二排的柜子建模:
同樣通過CSG來構(gòu)造柜體;
使用GridHelper來構(gòu)造網(wǎng)格;
同樣通過拼接圓柱體、立方體來創(chuàng)建柜子把手、柜子軌道
建模后的柜子如下圖所示:

第二排的其余柜子都是一樣的,只是Transform不同
最終,建模后的倉庫1如下圖所示:

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

Instanced Draw
因為倉庫1第一排柜子的抽屜數(shù)量較多(有1千多個),所以會造成drawcall過多(每個抽屜都是一個drawcall),這在移動端會有明顯的性能問題
因為有抽屜動畫(彈出抽屜),不能將其merge,所以使用Instanced Draw來批量繪制抽屜
值得注意的是抽屜可能是多材質(zhì)的Object3D(雖然此處是單材質(zhì)的Mesh),所以要將其中的每個Mesh拆分到一組Instanced Draw中。舉例來說,如果抽屜有個3個材質(zhì)(即3個子Mesh),則需要創(chuàng)建3個InstancedMesh,然后將所有抽屜的第一個材質(zhì)的Mesh對應(yīng)到第一個InstancedMesh中,其余的以此類推
燈光
使用RoomEnvironment作為environment map
Label
每個柜子上面需要顯示編號,如下圖所示:

它是billboard,通過Sprite來實現(xiàn)
實現(xiàn)人物
可以切換軌道相機(jī)和第三人稱相機(jī),后者會顯示人物。如下圖所示:

第三人稱相機(jī)的實現(xiàn)請參考Threejs 從零開始實現(xiàn)第三人稱漫游
人物我們是在這里下載的,帶骨骼動畫的FBX
使用AnimationMixer來播放人物的骨骼動畫,需要實現(xiàn)動畫的管理
人物需要與倉庫、柜子進(jìn)行碰撞檢測。我們使用Octree來構(gòu)造場景的八叉樹(只構(gòu)造一次,構(gòu)造墻、門、地面、柜體的八叉樹,為了性能考慮這里排除掉抽屜),人物則用Capsule作為包圍盒,通過檢測Capsule和八叉樹的相交來處理碰撞
彈出抽屜
雙擊倉庫1的第一排的柜子,可以進(jìn)入單個柜子的操作模式;然后點擊任意的抽屜,可彈出該抽屜,顯示綁定的數(shù)據(jù)。如下圖所示:

實現(xiàn)點擊的原理是:
我們將第一排的柜子的layer設(shè)置為可點擊的,并通過Raycaster與該layer進(jìn)行ray picking(這是優(yōu)化,以免與其余的物體進(jìn)行ray picking),并區(qū)分單擊和雙擊
進(jìn)入單個柜子的操作模式后,我們使用TrackControls、OrthographicCamera來正交地查看單個柜子,并隱藏其余的柜子
通過使用Tween來創(chuàng)建彈出、彈回抽屜的關(guān)鍵幀動畫。點擊單個抽屜時,播放彈出動畫。彈出后顯示綁定的數(shù)據(jù)(此處只顯示了柜子編號和抽屜編號,以后可通過后端請求來獲得相關(guān)的數(shù)據(jù))
UI
UI框架使用React+Redux
普通UI使用Antd
大屏UI使用DataV-React+ECharts
大屏UI如下圖所示:

“3D倉庫-倉庫1”這個Header、左側(cè)的餅圖是大屏UI
切換場景
能夠切換倉庫1、倉庫2場景,如下圖所示:

切換時,需要先深度刪除當(dāng)前場景的所有運行時數(shù)據(jù)(包括Mesh、Material、Geometry、Texture等),然后創(chuàng)建下一個場景的運行時數(shù)據(jù)
值得注意的是這兩個場景的資源只需加載一次
適配
對于移動端需要進(jìn)行適配
橫屏
移動端需要橫屏顯示。我們檢測如果是豎屏,則提示用戶橫屏
在微信中開啟橫屏的方法:
1、我->通用->開啟橫屏模式
2、開啟手機(jī)的自動旋轉(zhuǎn)
3、將手機(jī)橫屏
第三人稱在移動端顯示虛擬搖桿
如下圖所示:

通過nipplejs來實現(xiàn)虛擬搖桿
第三人稱相機(jī)在移動端的使用說明:
將左手拇指放到屏幕左方透明的操作桿上,右手拇指放到屏幕右方的任意位置。其中,左手控制人物移動,右手旋轉(zhuǎn)屏幕視角
IOS系統(tǒng)適配
IOS系統(tǒng)中值得注意的地方:
- 不應(yīng)該動態(tài)插入/刪除dom
- 構(gòu)造octree太慢
- 紋理大小不能太大(不能>=4096*4096)
要注意MMD材質(zhì)、粒子動畫中的紋理大小 - tga圖片最好轉(zhuǎn)成png圖片,因為大小可能太大
- MMD->PMXEditor->表情變換:
- 右下(其它)
- 帶“M |”前綴的表情([乘])可能會出問題,需要看情況刪除
- 左上(目)
- まばたき (眨眼 Blink)可能會出問題,需要看情況刪除
- 右下(其它)
- MMD->PMXEditor->材質(zhì) 中的紋理地址需要正確(如在壓縮紋理后,地址可能錯誤)
- 不支持.ogg音頻,支持.mp3音頻
大家好,本文使用three.js實現(xiàn)了3D倉庫一期項目,給出了代碼,分析了關(guān)鍵點,感謝大家~
浙公網(wǎng)安備 33010602011771號