基於 Threejs 的 web 3D 開發入門
導語
隨著軟硬體的發展,在PC和移動端瀏覽器上進行web 3D開發的條件已經基本成熟了,出現了不少js 3D庫,Threejs是js 3D庫中的佼佼者。國內也有企業開始做一些應用嘗試,某寶2016年雙11就用ThreeJS做了一個比較酷炫的3D宣傳頁面刷爆了朋友圈。
下圖是用Threejs繪製的一個3D立方體動畫的截圖,在這個demo裡,立方體會動態的旋轉,threeJS 30行程式碼就可以完成這麼一個demo。Threejs讓沒有豐富3D程式設計經驗的web前端開發人員,也可以快速上手開發web 3D應用。

image.png
Threejs是什麼
官網對Threejs的介紹非常簡單:“Javascript 3D library”。openGL是一個跨平臺3D/2D的繪圖標準,WebGL則是openGL在瀏覽器上的一個實現。web前端開發人員可以直接用WebGL介面進行程式設計,但WebGL只是非常基礎的繪圖API,需要程式設計人員有很多的數學知識、繪圖知識才能完成3D程式設計任務,而且程式碼量巨大。Threejs對WebGL進行了封裝,讓前端開發人員在不需要掌握很多數學知識和繪圖知識的情況下,也能夠輕鬆進行web 3D開發,降低了門檻,同時大大提升了效率。
Threejs應用場景舉例
1、web 3D遊戲

image.png
2、3D模型展示
下圖的例子中,使用者可以跟瀏覽器互動,通過滑鼠操作360度檢視汽車,點選車門進入到車內,檢視車內立體檢視,如同身臨其境。

image.png
3、資料視覺化

image.png
4、web vr

image.png
Threejs的基本要素
3D程式設計跟2D程式設計有較大不同,因此需要掌握一些3D程式設計的基本概念。Threejs的基本要素包括以下幾個方面:場景、相機、光、物體。
場景:是一個三維空間,所有物品的容器。可以把場景想象成一個空房間,接下來我們會往房間裡面放要呈現的物體、相機、光源。

image.png
相機:Threejs必須要有往場景中新增一個相機,相機用來確定觀察位置、方向、角度,相機看到的內容,就是我們最終在螢幕上看到的內容。在程式執行過程中,可以調整相機的位置、方向、角度。想象一下,在房間裡放了一個攝像機,你不在房間裡面,但可以遠端控制相機移動,攝像機傳給遠端電腦上展示出來的畫面,就是Threejs在螢幕上呈現的畫面。
光:假如沒有光,攝像機看不到任何東西,因此需要往場景中新增光源。為了跟真實世界更加接近,Threejs支援模擬不同光源,展現不同光照效果,有點光源、平行光、聚光燈、環境光等。
物體:有了場景、相機、光,就可以往場景中放物體了,在Threejs中,物體由形狀和材質兩部分組成,形狀決定物品的輪廓,材質則是物體的材料和質感。
渲染
Threejs繪製的東西,最終需要在螢幕一塊矩形畫布上顯示出來。為了實現動畫效果,我們需要有一個重繪機制。由於視神經元的反應速度問題,影象消失後仍然會在人眼殘留1/24秒,只要一秒內繪製的幀數超過24就能實現流暢的動畫效果。Threejs提供了重繪介面,我們有兩種方式去呼叫介面實現重繪。一種是setInterval,以固定的時間間隔去呼叫,可以用於我們對渲染幀數要求比較高的場景,但事實上由於Javascript是單執行緒的,這種方式並不能100%保證相同的時間間隔呼叫,如果瀏覽器繁忙可能會導致setInterval的延遲執行;第二種方式是requestAnimationFrame,讓瀏覽器自行根據當前cpu負載等情況決定何時重繪,達到最佳幀率。
位置
為了方便描述位置,引入了座標系,Threejs使用的是右手座標系,如下圖所示。座標系的原點位於渲染畫布的幾何中心。我們對物體的位置的描述,也是指物體的幾何中心的位置。

image.png
相機
相機有正交投影相機和透視投影相機兩種。透視投影跟人眼看到的世界是一樣的,近大遠小;正交投影則遠近都是一樣的大小,三維空間中平行的線,投影到二維空間也一定是平行的。大部分場景都適合使用透視投影相機,因為跟真實世界的觀測效果一樣;在製圖、建模等場景適合使用正交投影相機,方便觀察模型之間的大小比例。
Threejs中的相機跟真實世界的相機不完全一樣,這裡相機的可見區域是一個立方體,稱為相機的示景體。
正交投影相機
示景體是一個長方體,由6個引數確定:THREE.OrthographicCamera(left, right, top, bottom, near, far),這6個引數規定了相機示景體的左、右、上、下、前、後六個面的位置。

image.png
透視投影相機
示景體是一個梯形體,由四個引數確定:THREE.PerspectiveCamera(fov, aspect, near, far)

image.png
fov是相機在豎直方向的張角,aspect則是寬高比,即width/height,通常設為畫布的寬高比,near和far分別是近平面和遠平面與相機的距離。
投影的大小
考慮一種比較簡單的場景,相機示景體的遠近平面和座標系中的xy平面平行,從而示景體遠近平面上的內容剛好可以垂直投影到畫布上,並且示景體中與xy平面平行的任何一個平面,投影到畫布上剛好等於畫布大小。假如透視投影相機的近平面的大小為axb,遠平面大小為2ax2b,則一張axb大小的紙放在近平面上,投影到畫布時剛好鋪滿整張畫布;放到遠平面上則只能佔據畫布面積的1/4(遠平面的面積是近平面的4倍)。正是因為透視投影相機的示景體近小遠大,才會導致同樣一個物品放在不同位置顯示出近大遠小的效果。而正交投影相機因為遠近平面大小一樣,所以同一個物品距離相機的遠近不影響物體在畫布上投影展示的大小。
物體
物體由幾何形狀(Geometry)和材質(Material)組成。同樣的幾何形狀,不同材質構成了不同物體,比如球狀,有籃球、玻璃球、水晶球等。
形狀
Threejs提供了一些常見的幾何形狀,有三維的也有二維的,三維的比如長方體、球體、圓柱體、環等,二維的比如長方形、圓形、扇形等。如果預設提供的形狀不能滿足需求,也可以自定義,通過定義頂點和頂點之間的連線繪製自定義幾何形狀,更復雜的模型還可以用建模軟體建模後匯入。
計算機是如何繪製幾何形狀的呢?我們知道,計算機只能繪製直線,那麼曲線和3D形狀如何繪製出來呢?
1、繪製圓形。如下圖所示,通過繪製多邊形實現近似的圓形效果,當多邊形的邊數足夠多的時候,兩條邊之間的過渡就顯得平滑,多邊形看起來就足夠圓了。

image.png
2、繪製3D模型。常用的做法是用三角形組成的網格來模擬,如下圖所示,用足夠多的三角形時,兔子的身體看起來就足夠平滑,跟真實兔子比較接近。著名的斯坦福兔子模型用了69451個三角形。

image.png
材質
Threejs提供了幾種比較有代表性的材質,常用的有漫反射、鏡面反射兩種材質,還可以引入外部圖片,貼到物體表面,稱為紋理貼圖。
外部模型
現實世界豐富多彩,不是Threejs的幾種預設幾何形狀和材質所能表達的,實際運用中,很多時候需要用到外部模型,通過3D建模軟體構建物體的三維模型並匯出模型檔案,Threejs匯入模型檔案進行使用。

image.png
光照
光源主要是以下幾種:1)、環境光,所有角度看到的亮度一樣,通常用來為整個場景指定一個基礎亮度,沒有明確光源位置;2)、點光源,一個點發出的光源,照到不同物體表面的亮度線性遞減;3)、平行光,亮度與光源和物體之間的距離無關,只與平行光的角度和物體所在平面有關;4)、聚光燈,投射出的是類似圓錐形的光線。
小結
本文對Threejs程式設計做了一下簡單介紹,目的是讓讀者對Threejs程式設計有個基本認識。紙上得來終覺淺,絕知此事要躬行!建議到Threejs官網首頁看看那些有趣的demo,著手寫一些簡單的demo進行實踐。目前web 3D應用因為瀏覽器渲染效能、模型體積過大等原因,並沒有大規模使用起來,只限於在品牌宣傳等部分領域嘗試使用。我剛好經歷過瀏覽器2D資料視覺化繪圖由flash向JS轉變的過程(2012年前後),相信隨著軟硬體效能的提升和網路速度的提升,web 3D應用也會慢慢的推廣使用起來。