本文來自網(wǎng)易云社區(qū)
作者:唐釗
項目背景
那是在一個毫無征兆的下午俱病,我還沉浸在 vue 的世界中,突然編輯跑過來說N的新官網(wǎng)想做一些3D全景的東西搂漠,一開始其實我的內(nèi)心是拒絕的迂卢,一是沒怎么實質(zhì)性做過 WEBGL 的東西,只是組內(nèi) mini 項目的時候看了看基礎(chǔ)的內(nèi)容,再者當時也很忙冷守,感覺這東西聽起來就很復雜刀崖,隨著項目開發(fā)結(jié)束在此寫下這篇文章,記錄一下自己在開發(fā)過程中遇到的問題和解決的思路以供備忘拍摇。
準備工作
開發(fā)使用: WS亮钦、threejs-r82,3DsMax;
最初遇到的問題:其實3D 全景其實并不一定非要使用 webgl充活,css3同樣可以做到蜂莉,但是考慮到后續(xù)要增加模型動畫的問題,所以不得不用 webgl 了混卵。那么 webgl 到底是什么呢映穗?WebGL是一種3D繪圖標準,這種繪圖技術(shù)標準允許把JavaScript和OpenGL ES 2.0結(jié)合在一起幕随,通過增加OpenGL ES 2.0的一個JavaScript綁定蚁滋,WebGL可以為HTML5 Canvas提供硬件3D加速渲染,這樣Web開發(fā)人員就可以借助系統(tǒng)顯卡來在瀏覽器里更流暢地展示3D場景和模型了赘淮,還能創(chuàng)建復雜的導航和數(shù)據(jù)視覺化辕录。由于webgl是基于OpenGL和JavaScript技術(shù)結(jié)合,而OpenGL與微軟DirectX存在著競爭關(guān)系梢卸,而且微軟自身也開發(fā)了Silverlight插件走诞,與webgl其實是類似的,所以微軟對webgl技術(shù)并不支持蛤高。但是最新的 IE11和 edge 瀏覽器是 ok 的蚣旱。所以一開始的難點就在于怎么去構(gòu)建這個3D的場景和內(nèi)部的模型動畫等。(特此說明:本文不會講解 threejs 的基本知識戴陡,所以以下內(nèi)容需要一定的 webgl 基礎(chǔ)塞绿,見下面鏈接)
先安利一款 chrome 的 threeJs 插件?https://chrome.google.com/webstore/detail/threejs-inspector/dnhjfclbfhcbcdfpjaeacomhbdfjbebi?賊好用!
開發(fā)中碰到的問題
整個場景分為3大部分猜欺,最外層的天空位隶,中間的建筑,以及內(nèi)部的各種燈籠开皿,蝴蝶,魚等等模型動畫篮昧,所以我們需要分布考慮各個場景如何構(gòu)建赋荆,首先是最外層的天空和建筑,因為 N 項目組有特殊的場景編輯工具懊昨,可以非常方便的輸出場景的魚眼圖窄潭,所以我打算利用這個魚眼圖直接構(gòu)建一個球形,也就是構(gòu)建了2個球形酵颁,一內(nèi)一外嫉你,分別為天空和建筑月帝,讓天空的球形做 圍繞 Y 軸的TWEEN動畫,形成天空在轉(zhuǎn)動的效果幽污,同時內(nèi)部的建筑使用 png 貼圖嚷辅,對材質(zhì)增加 transparent:true 的選項,讓天空可見距误,如果你不加透明度這個選項簸搞,png 貼圖的透明部分是會變成白色的。
外面兩層的球形模型很好搭建准潭,接下來的問題就來了趁俊,如何做模型動畫,因為大家都從來沒做過刑然,所以一開始我是一籌莫展寺擂,因為這個玩意壓根不知道從何下手,后面了解到模型動畫需要由專門的動畫師去建模然后貼圖并且制作動畫泼掠,導出相應的文件由前端放進場景里面去怔软,所以我就興致沖沖的讓游戲那邊的動畫導出 dae 文件給我,為什么導出 dae 呢武鲁,因為我看 threejs 的示例里面是用的 dae 的模型爽雄,所以當時想當然的認為只要導出 dae 就能放進去了,事實證明沐鼠,我還是 too young too simple. 為什么挚瘟,我看著官方的示例那天下午一遍一遍的問自己,為什么我的就不行饲梭,難道我導出的模型就不是模型乘盖,于是我又是一頓查資料,那一刻憔涉,搜狗百度谷歌全上陣订框,可是依舊苦苦無果,打算去研究官方的 dae 文件兜叨,可以導入max發(fā)現(xiàn)全亂掉了穿扳,根本沒法下手,終于在第二天中午快要吃飯的時候在一個 max 論壇發(fā)現(xiàn)一個哥兒們的一段話讓我們恍然大悟国旷,他的原話我記不清了矛物,不過中心意思就一個:web 所用的 dae 文件需要模型全部塌陷以后重新?lián)夜趋溃⑶也荒苡苗R像骨骼跪但,我那個去履羞,敲代碼的我肯定不懂這是什么意思啊,但是從這里面肯定說明了直接導出的 dae 文件就是有問題,于是我去問了我們組的動畫師忆首,她看了看說:“原來是這樣爱榔,那我試試吧”。于是我們的動畫師開始了各種嘗試糙及,給了我各種文件進行導入详幽,一開始模型是可以正常導入了,但是動畫總是不對丁鹉,要么骨骼信息各種丟失妒潭,要么就是各種奇葩的抖動頻率,我們再一次被打擊了揣钦,后面再查文檔時發(fā)現(xiàn) R7X 以上的動畫調(diào)用和 R6X 的發(fā)生了變化雳灾,于是我進行了一定的代碼更改,oh my god 終于在場景里面看到了一個活的模型了冯凹,接著我們又開始研究貼圖和自發(fā)光問題谎亩。
總的來說上面這一部分是當時最打擊信心以及最費時間的一部分了,因為吃螃蟹總是不容易的宇姚,不過后續(xù)發(fā)現(xiàn)還有很多其他的問題等著自己匈庭,首先是模型由于沒有燈光,顯得很暗淡浑劳,所以需要增加一個光源阱持,由于場景里面的背景是一張圖片,如果增加一個照射范圍很大的光源勢必會影響到圖片魔熏,會改變圖片的曝光衷咽,所以只能去研究模型的自發(fā)光,后來發(fā)現(xiàn)還是比較簡單蒜绽,需要動畫師給我一個合適的色值镶骗,設(shè)置自身的 color 和 emissive 即可。
場景中的花瓣采用的是粒子發(fā)射器生成的隨機粒子躲雅,添加花瓣的紋理鼎姊,花瓣的降落由隨機數(shù)生成在一個合適的值。但是由于粒子本身是沒有 XYZ 的相赁,所以沒法做旋轉(zhuǎn)相寇。
場景中的貼圖由于 webgl 的同源策略,所以不能在跨域的情況下使用貼圖钮科。
場景中的字體問題裆赵,在 threejs 中默認是不支持中文字體的,只能使用英文跺嗽,原因嘛,你懂得,中文字符太多了桨嫁,所以如果項目中需要在場景中使用中文植兰,可以參考該文章?地址,另外如果不是非得使用最好還是用英文吧,因為這加載的都是流量啊璃吧。
最后是場景內(nèi)的鼠標事件楣导,如果你沒有做過你一定會一臉懵逼,因為這里面根本沒有什么 click 事件畜挨,就比如一個鼠標移入到可點擊物體上變成手型都不知道怎么搞筒繁,然后找資料,知道了ThreeJS提供了一個 raycaster的API用于返回用戶光標所在位置的所有3維元素巴元,它的實現(xiàn)原理是在屏幕上某個二維坐標點與相機位置和視角形成的向量方向上投射一條射線毡咏,返回與射線相交的所有三維物體的集合,集合的第一個物體為距離相機最近的物體逮刨,最后一個則為離相機最遠的呕缭。當使用拾取器去獲取用戶點擊的物體時,需要事先將所有可參與用戶交互的三維物體放到一個集合里修己。在創(chuàng)建拾取器后獲取兩個集合的交集恢总,即當前用戶在屏幕點擊的位置上所有被設(shè)置為可被選擇的物體,第一個即可視為用戶直接點擊的物體睬愤。所以在場景內(nèi)部的所有交互都只能通過射線的方式去完成片仿,當然 github 上也有一些封裝的場景物體點擊的事件庫,其原理也是基于射線尤辱。但是由于上面提到的 png 貼圖問題砂豌,我讓動畫師將交互對象也做成了動畫模型,然后我就發(fā)現(xiàn)為什么我的射線發(fā)射過去沒有交集了啥刻,一開始以為是我鼠標有問題奸鸯,但是后來重現(xiàn)了幾次發(fā)現(xiàn)確實是動畫模型就不行,普通的 mesh 就 OK可帽,那么問題就肯定在動畫上面了娄涩,去 github 上找了很久看到了有人和我有同樣的問題。?傳送門映跟,回答者說skinnedMesh 已經(jīng)從射線集合中移除了蓄拣,老版本曾經(jīng)有支持,所以我只能想想其他的方法努隙,最后采用了在動畫模型前面添加了一個普通的 mesh球恤,將其透明掉,這樣射線就有了交集荸镊。能完美的點擊到了物體咽斧。
最后的話
至此整個場景內(nèi)部的物件基本上交互的功能和動畫功能都 OK 了堪置,但是還有很多之前研究的內(nèi)容最后沒有放到整個項目中去,比如攝像機的移動旋轉(zhuǎn)张惹,場景更濃厚的氣氛渲染舀锨,一是受限于web 瀏覽器,說實話 web 上搞3D 本來就不是太好宛逗,又卡消耗又大坎匿,而是確實時間也不長,前前后后所有的算滿也才26天雷激,這其中還要花很多時間整合雙端的新聞發(fā)布系統(tǒng)替蔬,還要做其他的整合適配等等,所以后面空了還是要再看看 threeJs 的東西屎暇,增強一下自己的技術(shù)棧承桥,同時更深入的理解 webgl。