ThreeJs 3D 全景專案開發總結

網易雲社群發表於2018-09-27

本文來自網易雲社群

作者:唐釗


專案背景

那是在一個毫無徵兆的下午,我還沉浸在 vue 的世界中,突然編輯跑過來說N的新官網想做一些3D全景的東西,一開始其實我的內心是拒絕的,一是沒怎麼實質性做過 WEBGL 的東西,只是組內 mini 專案的時候看了看基礎的內容,再者當時也很忙,感覺這東西聽起來就很複雜,隨著專案開發結束在此寫下這篇文章,記錄一下自己在開發過程中遇到的問題和解決的思路以供備忘。


準備工作

開發使用: WS、threejs-r82,3DsMax;

最初遇到的問題:其實3D 全景其實並不一定非要使用 webgl,css3同樣可以做到,但是考慮到後續要增加模型動畫的問題,所以不得不用 webgl 了。那麼 webgl 到底是什麼呢?WebGL是一種3D繪圖示準,這種繪圖技術標準允許把JavaScript和OpenGL ES 2.0結合在一起,通過增加OpenGL ES 2.0的一個JavaScript繫結,WebGL可以為HTML5 Canvas提供硬體3D加速渲染,這樣Web開發人員就可以藉助系統顯示卡來在瀏覽器裡更流暢地展示3D場景和模型了,還能建立複雜的導航和資料視覺化。由於webgl是基於OpenGL和JavaScript技術結合,而OpenGL與微軟DirectX存在著競爭關係,而且微軟自身也開發了Silverlight外掛,與webgl其實是類似的,所以微軟對webgl技術並不支援。但是最新的 IE11和 edge 瀏覽器是 ok 的。所以一開始的難點就在於怎麼去構建這個3D的場景和內部的模型動畫等。(特此說明:本文不會講解 threejs 的基本知識,所以以下內容需要一定的 webgl 基礎,見下面連結)

threejs基礎知識詳解

先安利一款 chrome 的 threeJs 外掛 chrome.google.com/webstore/de… 賊好用!


開發中碰到的問題

  • 整個場景分為3大部分,最外層的天空,中間的建築,以及內部的各種燈籠,蝴蝶,魚等等模型動畫,所以我們需要分佈考慮各個場景如何構建,首先是最外層的天空和建築,因為 N 專案組有特殊的場景編輯工具,可以非常方便的輸出場景的魚眼圖,所以我打算利用這個魚眼圖直接構建一個球形,也就是構建了2個球形,一內一外,分別為天空和建築,讓天空的球形做 圍繞 Y 軸的TWEEN動畫,形成天空在轉動的效果,同時內部的建築使用 png 貼圖,對材質增加 transparent:true 的選項,讓天空可見,如果你不加透明度這個選項,png 貼圖的透明部分是會變成白色的。

  • 外面兩層的球形模型很好搭建,接下來的問題就來了,如何做模型動畫,因為大家都從來沒做過,所以一開始我是一籌莫展,因為這個玩意壓根不知道從何下手,後面瞭解到模型動畫需要由專門的動畫師去建模然後貼圖並且製作動畫,匯出相應的檔案由前端放進場景裡面去,所以我就興致沖沖的讓遊戲那邊的動畫匯出 dae 檔案給我,為什麼匯出 dae 呢,因為我看 threejs 的示例裡面是用的 dae 的模型,所以當時想當然的認為只要匯出 dae 就能放進去了,事實證明,我還是 too young too simple. 為什麼,我看著官方的示例那天下午一遍一遍的問自己,為什麼我的就不行,難道我匯出的模型就不是模型,於是我又是一頓查資料,那一刻,搜狗百度谷歌全上陣,可是依舊苦苦無果,打算去研究官方的 dae 檔案,可以匯入max發現全亂掉了,根本沒法下手,終於在第二天中午快要吃飯的時候在一個 max 論壇發現一個哥兒們的一段話讓我們恍然大悟,他的原話我記不清了,不過中心意思就一個:web 所用的 dae 檔案需要模型全部塌陷以後重新撘骨骼,並且不能用映象骨骼,我那個去,敲程式碼的我肯定不懂這是什麼意思啊,但是從這裡面肯定說明了直接匯出的 dae 檔案就是有問題,於是我去問了我們組的動畫師,她看了看說:“原來是這樣,那我試試吧”。於是我們的動畫師開始了各種嘗試,給了我各種檔案進行匯入,一開始模型是可以正常匯入了,但是動畫總是不對,要麼骨骼資訊各種丟失,要麼就是各種奇葩的抖動頻率,我們再一次被打擊了,後面再查文件時發現 R7X 以上的動畫呼叫和 R6X 的發生了變化,於是我進行了一定的程式碼更改,oh my god 終於在場景裡面看到了一個活的模型了,接著我們又開始研究貼圖和自發光問題。

  • 總的來說上面這一部分是當時最打擊信心以及最費時間的一部分了,因為吃螃蟹總是不容易的,不過後續發現還有很多其他的問題等著自己,首先是模型由於沒有燈光,顯得很暗淡,所以需要增加一個光源,由於場景裡面的背景是一張圖片,如果增加一個照射範圍很大的光源勢必會影響到圖片,會改變圖片的曝光,所以只能去研究模型的自發光,後來發現還是比較簡單,需要動畫師給我一個合適的色值,設定自身的 color 和 emissive 即可。

  • 場景中的花瓣採用的是粒子發射器生成的隨機粒子,新增花瓣的紋理,花瓣的降落由隨機數生成在一個合適的值。但是由於粒子本身是沒有 XYZ 的,所以沒法做旋轉。

  • 場景中的貼圖由於 webgl 的同源策略,所以不能在跨域的情況下使用貼圖。

  • 場景中的字型問題,在 threejs 中預設是不支援中文字型的,只能使用英文,原因嘛,你懂得,中文字元太多了,所以如果專案中需要在場景中使用中文,可以參考該文章 地址,另外如果不是非得使用最好還是用英文吧,因為這載入的都是流量啊。

  • 最後是場景內的滑鼠事件,如果你沒有做過你一定會一臉懵逼,因為這裡面根本沒有什麼 click 事件,就比如一個滑鼠移入到可點選物體上變成手型都不知道怎麼搞,然後找資料,知道了ThreeJS提供了一個 raycaster的API用於返回使用者游標所在位置的所有3維元素,它的實現原理是在螢幕上某個二維座標點與相機位置和視角形成的向量方向上投射一條射線,返回與射線相交的所有三維物體的集合,集合的第一個物體為距離相機最近的物體,最後一個則為離相機最遠的。當使用拾取器去獲取使用者點選的物體時,需要事先將所有可參與使用者互動的三維物體放到一個集合裡。在建立拾取器後獲取兩個集合的交集,即當前使用者在螢幕點選的位置上所有被設定為可被選擇的物體,第一個即可視為使用者直接點選的物體。所以在場景內部的所有互動都只能通過射線的方式去完成,當然 github 上也有一些封裝的場景物體點選的事件庫,其原理也是基於射線。但是由於上面提到的 png 貼圖問題,我讓動畫師將互動物件也做成了動畫模型,然後我就發現為什麼我的射線發射過去沒有交集了,一開始以為是我滑鼠有問題,但是後來重現了幾次發現確實是動畫模型就不行,普通的 mesh 就 OK,那麼問題就肯定在動畫上面了,去 github 上找了很久看到了有人和我有同樣的問題。 傳送門,回答者說skinnedMesh 已經從射線集合中移除了,老版本曾經有支援,所以我只能想想其他的方法,最後採用了在動畫模型前面新增了一個普通的 mesh,將其透明掉,這樣射線就有了交集。能完美的點選到了物體。


最後的話

至此整個場景內部的物件基本上互動的功能和動畫功能都 OK 了,但是還有很多之前研究的內容最後沒有放到整個專案中去,比如攝像機的移動旋轉,場景更濃厚的氣氛渲染,一是受限於web 瀏覽器,說實話 web 上搞3D 本來就不是太好,又卡消耗又大,而是確實時間也不長,前前後後所有的算滿也才26天,這其中還要花很多時間整合雙端的新聞釋出系統,還要做其他的整合適配等等,所以後面空了還是要再看看 threeJs 的東西,增強一下自己的技術棧,同時更深入的理解 webgl。


網易雲免費體驗館,0成本體驗20+款雲產品!

更多網易研發、產品、運營經驗分享請訪問網易雲社群


相關文章:
【推薦】 淺析Kubernetes的工作原理


相關文章