天天看點

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

點選檢視第一章 點選檢視第三章

第2章

建構Three.js應用的基本元件

在第1章中我們介紹了Three.js庫的基礎知識。通過示例展示了Three.js是如何工作的,然後建立了第一個完整的Three.js應用。在本章中我們将會深入了解Three.js庫,介紹建構Three.js應用的基本元件。通過本章你将了解以下内容:

  • 在Three.js應用中使用的主要元件。
  • THREE.Scene對象的作用。
  • 幾何圖形和網格是如何關聯的。
  • 正交投影錄影機和透視投影錄影機的差別。

我們首先來介紹如何建立場景并添加對象。

2.1 建立場景

在第1章中我們已經建立了一個THREE.Scene,想必你已經了解了Three.js庫的基礎知識。我們可以看到,一個場景想要顯示任何東西,需要表2.1所示三種類型的元件。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

THREE.Scene對象是所有不同對象的容器,但這個對象本身沒有那麼多的選項和函數。

THREE.Scene對象有時被稱為場景圖,可以用來儲存所有圖形場景的必要資訊。在Three.js中,這意味着THREE.Scene儲存所有對象、光源和渲染所需的其他對象。有趣的是,場景圖,顧名思義,不僅僅是一個對象數組,還包含了場景圖樹形結構中的所有節點。每個你添加到Three.js場景的對象,甚至包括THREE.Scene本身,都是繼承自一個名為THREE.Object3D的對象。一個THREE.Object3D對象也可以有自己的子對象,你也可以使用它的子對象來建立一個Three.js能解釋和渲染的對象樹。

2.1.1 場景的基本功能

了解一個場景功能的最好方法就是看示例。在本章的源代碼中,你可以找到一個名為 01-basic-scene.html的例子。我将使用這個例子來解釋一個場景所擁有的各種方法和選項。當我們在浏覽器中打開這個示例的時候,其效果大緻如圖2.1所示。請記住除了滑鼠之外,鍵盤上的A、S和D鍵也可用于在渲染場景中轉向、縮放和平移。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

這跟我們在第1章中看到的例子非常像。盡管這個場景看上去有點兒空蕩蕩,但其實它已經包含了好幾個對象。通過下面的源代碼可以看到,我們使用THREE.Scene對象的scene.add(object)方法添加了一個THREE.Mesh對象(你看到的平面)、一個THREE.SpotLight對象(聚光燈光源)和一個THREE.AmbientLight對象(環境光)。渲染場景的時候,THREE.Camera對象會自動地被Three.js添加進來。但是我們手動添加它會是一個更好的實踐,尤其是當你需要處理多個錄影機的時候。部分源碼如下:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章
帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在深入了解THREE.Scene對象之前,先說明一下你可以在這個示例中做些什麼,然後我們再來看代碼。在浏覽器中打開01-basic-scene.html示例,看看右上角的那些控件。如圖2.2所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

通過這些控件,你可以往場景中添加方塊、移除最後一個添加到場景中的方塊以及在浏覽器控制台中顯示目前場景中的所有對象。控件區的最後一項顯示了目前場景中所有對象的數量。你可能會發現場景在啟動的時候就已經包含了4個對象,它們是地面、環境光、點光源和我們之前提到的錄影機。讓我們從最簡單的addCube方法開始逐一了解控件區的所有方法。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章
帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

現在這段代碼應該很容易讀懂,因為這裡沒有引入很多新的概念。當你點選addCube按鈕的時候,一個新的THREE.BoxGeometry對象就會被建立,它的長、寬和高都是一個從1到3之間的随機數。除了尺寸是随機的,這個方塊的顔色和位置也是随機的。

這段代碼裡的新東西是我們使用name屬性為整個方塊指定了一個名字。方塊的名字是在cube後面加上目前場景中對象的數量(即scene.children.length)。給對象命名在調試的時候是很有用的,而且還可以直接通過名字來擷取場景中的對象。如果使用Three.Scene.getObjectByName(name)方法,可以直接擷取場景中名為name的對象,然後可以執行一些比如改變位置的操作。你或許想知道最後一行代碼的作用,我們的控制界面就是使用numberOfObjects變量來顯示場景中對象數量的。是以,無論什麼時候添加或者删除對象,我們都要将該變量設定為更新後的數量。

在控制界面中能夠調用的另一個方法是removeCube()。顧名思義,點選removeCube按鈕,将會移除最後一個添加到場景中的方塊。代碼實作如下所示:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在場景中添加對象使用的是add()方法,而從場景中移除對象就要使用remove()方法。由于Three.js将子對象儲存在數組中(最新的對象儲存在數組的最後),是以我們可以使用THREE.Scene對象的children屬性來擷取最後一個添加到場景中的對象,children屬性将場景中的所有對象存儲為數組。在移除對象時,我們還需要檢查該對象是不是THREE.Mesh對象,這樣做的原因是避免移除錄影機和光源。當我們移除對象之後,需要再次更新控制界面中表示場景中對象數量的屬性numberOfObjects。

控制界面上的最後一個按鈕的标簽是outputObjects。你或許已經發現在點選該按鈕後什麼都沒有發生。因為這個按鈕的功能是在浏覽器的控制台中列印出場景中的所有對象資訊,如圖2.3所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

我們使用的是内置的console對象在浏覽器控制台日志中輸出對象資訊,代碼如下所示:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

這樣做對于代碼調試是非常有用的,尤其是當你為對象命名時。它對查找某個特定對象相關的問題是非常有用的。例如,對象cube-17(如果你已經知道對象的名字,就可以使用console.log(scene.getObjectByName("cube-17"))來輸出對應的資訊。輸出結果如圖2.4所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

目前為止,我們已經學習了如下一些和場景相關的方法:

  • THREE.Scene.Add:用于向場景中添加對象
  • THREE.Scene.Remove:用于移除場景中的對象
  • THREE.Scene.children:用于擷取場景中所有的子對象清單
  • THREE.Scene.getObjectByName:利用name屬性,用于擷取場景中特定的對象

這些方法是和場景相關的重要方法,通常情況下這些方法就可以滿足大部分需求了。但是,還有幾個輔助方法可能會被用到,請看下面的示例代碼片段。

如你在第1章中所看到的,我們使用了render循環來渲染場景。該循環代碼如下所示:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在這裡,我們使用了THREE.Scene.traverse()方法。我們可以将一個方法作為參數傳遞給traverse()方法,這個傳遞來的方法将會在每一個子對象上執行。由于THREE.Scene對象存儲的是對象樹,是以如果子對象本身還有子對象,traverse()方法會在所有的子對象上執行,直到周遊完場景樹中的所有對象為止。

我們使用render()方法來更新每個方塊的旋轉狀态(我們特意忽略了表示地面的plane對象)。我們還可以使用for循環或者forEach來周遊children屬性數組來達到同樣的目的,因為隻向THREE.Scene增加對象且沒有建立嵌套結構。

在我們深入讨論THREE.Mesh和THREE.Geometry對象之前,先來介紹THREE.Scene對象的兩個屬性:fog(霧化)和overrideMaterial(材質覆寫)。

2.1.2 給場景添加霧化效果

使用fog屬性就可以為整個場景添加霧化效果。霧化效果是:場景中的物體離錄影機越遠就會變得越模糊。如圖2.5所示。

為了更好地觀察霧化效果,可以利用滑鼠推進或拉遠錄影機,這樣就可以觀察物體是如何受霧化效果的影響。在Three.js中為場景添加霧化效果是很簡單的,在定義完場景後隻要添加如下代碼即可:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章
帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

我們在這裡定義一個白色霧化效果(0xffffff)。後面的兩個參數是用來調節霧的顯示,0.015是near(近處)屬性的值,100是far(遠處)屬性的值。通過這兩個屬性可以決定霧化開始和結束的地方,以及加深的程度。使用THREE.Fog建立的對象,霧的濃度是線性增長的,除此之外還有另外一種添加霧化效果的方法,定義如下:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在這個方法中不再指定near和far屬性,隻需要設定霧的顔色(0xffffff)和濃度(0.01)即可。需要注意的是,該方法中霧的濃度不再是線性增長的,而是随着距離呈指數增長。

2.1.3 使用overrideMaterial屬性

我們要介紹的最後一個場景屬性是overrideMaterial。當設定了overrideMaterial屬性後,場景中所有的物體都會使用該屬性指向的材質,即使物體本身也設定了材質。當某一個場景中所有物體都共享同一個材質時,使用該屬性可以通過減少Three.js管理的材質數量來提高運作效率,但是實際應用中,該屬性通常并不非常實用。該屬性的使用方法如下所示:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

使用了上述代碼中顯示的overrideMaterial屬性之後,場景的渲染結果如圖2.6所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

從圖中可以看出,所有的立方體都使用相同的材質和顔色進行渲染。在這個示例中,我們用的材質是THREE.MeshLambertMaterial,而且使用該材質類型,還能夠建立出不發光但是可以對場景中的光源産生反應的物體。在第4章中你将會學到更多關于這種材質的内容。

在本節中我們介紹了Three.js中最核心的概念:場景。關于場景我們需要記住的是:它是在渲染時你想使用的所有物體、光源的容器。表2.2列出了THREE.Scene中最常用的方法和屬性。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在下一節中,我們将會對可以添加到場景中的物體作詳細介紹。

2.2 幾何體和網格

在前面的例子中我們已經使用了幾何體和網格。比如在向場景中添加球體時,代碼如下所示:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

我們使用THREE.SphereGeometry定義了物體的形狀、使用THREE.MeshBasicMaterial定義了物體的外觀和材質,并将它們合并成能夠添加到場景中的網格(THREE.Mesh)。在這一節中将進一步介紹什麼是幾何體和網格。

2.2.1 幾何體的屬性和方法

Three.js提供了很多可以在三維場景中使用的幾何體。圖2.7是04-geometries示例的截圖,該圖展示了Three.js庫中可用的标準幾何體。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在第5章和第6章中我們将會深入讨論Three.js提供的所有基本幾何體和進階幾何體。在這一節中,我們主要介紹什麼是幾何體。

像其他大多數三維庫一樣,在Three.js中幾何體基本上是三維空間中的點集(也被稱作頂點)和将這些點連接配接起來的面。以立方體為例:

  • 一個立方體有8個角。每個角都可以用x、y和z坐标點來定義,是以每個立方體在三維空間中都有8個點。在Three.js中,這些點稱為頂點。
  • 一個立方體有6個面,每個角有一個頂點。在Three.js中,每個面都是包含3個頂點的三角形。是以,立方體的每個面都是由兩個三角形面組成的。

當你使用Three.js庫提供的幾何體時,不需要自己定義幾何體的所有頂點和面。對于立方體來說,你隻要定義長、寬、高即可。Three.js會基于這些資訊在正确的位置建立一個擁有8個頂點和12個三角形面的立方體。盡管可以使用Three.js提供的幾何體,但是你仍然可以通過定義頂點和面來自定義建立幾何體。建立幾何體的方法如下代碼所示:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

上述代碼展示了如何建立簡單的立方體。在vertices數組中儲存了構成幾何體的頂點,在faces數組中儲存了由這些頂點連接配接起來建立的三角形面。如new THREE.Face3(0,2,1)就是使用vertices數組中的點0、2和1建立而成的三角形面。需要注意的是建立面的頂點時的建立順序,因為頂點順序決定了某個面是面向錄影機還是背向錄影機的。如果你想建立面向錄影機的面,那麼頂點的順序是順時針的,反之頂點的順序是逆時針的。

在這個示例中,我們使用THREE.Face3元素定義立方體的6個面,也就是說每個面都是由兩個三角形面組成的。在Three.js以前的版本中,可以使用四邊形來定義面。到底是使用四邊形還是三角形來建立面,在三維模組化領域裡一直存在比較大的争議。基本上,大家都習慣于用四邊形來建立面,因為它比三角形更容易增強和平滑。但是對于渲染器和遊戲引擎來說,使用三角形更加容易,因為三角形渲染起來效率更高。

有了這些頂點和面,我們就可以建立一個新的THREE.Geometry的執行個體對象,然後将vertices數組指派給vertices屬性,将faces數組指派給faces屬性。最後我們需要做的就是在建立的幾何體上執行computeFaceNormals()方法,當該方法執行時,Three.js會決定每個面的法向量,法向量用于決定不同光源下的顔色。

基于幾何體,我們就可以建立網格了。我已經建立了一個例子,你可以嘗試修改頂點的位置來體驗一下。在示例05-custom-geometry.html中,你可以修改立方體的每個頂點并能夠看到相應的面是如何變化的。如圖2.8所示(按H鍵來隐藏GUI)。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

這個示例和其他示例一樣,都有一個render循環。無論何時修改了頂點的屬性,立方體都會基于修改後的值重新進行渲染。出于性能方面的考慮,Three.js認為組成網格的幾何體在整個生命周期内是不會改變的,而且對于大部分的幾何體而言,這個假設是成立的。為了使我們的示例工作,我們還需要在render循環中添加如下的代碼:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在循環中的第一行,我們将組成網格的幾何體的vertices屬性值指向一個更新後的頂點數組。如果頂點數組vertices沒有更新,不需要重新配置這些面,因為它們仍然連接配接到原來的頂點。如果頂點被更新了,我們還需要告訴幾何體頂點需要更新,在代碼中是将verticesNeedUpdate屬性設定為true來實作這一點的,最後需要調用computeFaceNormals()方法來重新計算每個面,進而完成整個模型的更新。

我們将要介紹的最後一個關于幾何體的函數是clone()。我們說過幾何體可以定義物體的形狀,添加相應的材質後就可以建立出能夠添加到場景中并由Three.js渲染的物體。通過clone()方法我們可以建立出幾何體對象的拷貝。為這些拷貝對象添加不同的材質,我們就可以建立出不同的網格對象。在示例05-custom-geometry.html裡,你可以在控制界面的頂端看到一個clone按鈕。如圖2.9所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

如果點選clone按鈕就可以按照幾何體目前的狀态建立出一個拷貝,而且這個新對象被賦予了新的材質并被添加到場景中。實作這個功能的代碼是非常簡單的,但是由于我使用的材質導緻代碼看起來有點複雜。首先我們先看一下綠色材質的實作代碼:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

如你所看到的,我們使用的不是一個材質,而是由兩個材質構成的數組。這樣做的原因是,除了顯示綠色透明的立方體外,我還想顯示一個線框。因為使用線框可以很容易地找出頂點和面的位置。當然,Three.js支援使用多種材質來建立網格。你可以使用SceneUtils.createMulti-MaterialObject()方法來達到這個目的。代碼如下所示:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

這個方法建立的并不是一個THREE.Mesh對象執行個體,而是為materials數組中每個指定的材質建立一個執行個體,并把這些執行個體存放在一個組裡(THREE.Object3D對象)。你可以像使用場景中的對象那樣使用這個組,如添加網格、按名稱擷取對象等。如果要為這個組中所有的子對象添加陰影,我們可以這樣做:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

現在,我們繼續讨論clone()函數:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

點選clone按鈕,這段JavaScript代碼就會被調用。這裡我們複制立方體的第一個子對象。請記住,mesh變量包含兩個THREE.Mesh子對象:基于兩個不同材質建立的。通過這個複制的幾何體我們建立了一個新的網格,并命名為mesh2。使用translate()方法移動這個新建立的網格,删除之前的副本(如果存在)并把這個副本添加到場景中。

在前面的章節中,我們使用THREE.SceneUtils對象的createMultiMaterialObject()方法為幾何體添加了線框。在Three.js中還可以使用THREE.WireframeGeometry來添加線框。假設有一個幾何體對象名為geom,可以通過下面代碼基于geom建立一個線框對象:

var wireframe = new THREE.WireframeGeometry(geom);

然後,基于建立的線框對象建立一個Three.LineSegments對象并将它添加到場景中:

var line = new THREE.LineSegments(wireframe);

scene.add(line);

最後便可以利用它來繪制線框了,并且還可以像下面代碼那樣設定線框的寬度:

line.material.linewidth = 2;

關于Three.js中幾何體的知識,我們暫時就學習到這裡。

2.2.2 網格對象的屬性和方法

我們已經知道,建立一個網格需要一個幾何體,以及一個或多個材質。當網格建立好之後,我們就可以将它添加到場景中并進行渲染。網格對象提供了幾個屬性用于改變它在場景中的位置和顯示效果。下面我們來看下網格對象提供的屬性和方法,具體見表2.3。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

我們同樣準備了一個示例,你可以修改這些屬性的值來感受下效果。當你在浏覽器中打開示例06-mesh-properties.html時,可以看到一個下拉菜單。通過該下拉菜單你就可以修改屬性的值,并立即看到修改後的效果,如圖2.10所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

下面來逐一講解這些屬性和方法,先從position屬性開始。通過這個屬性你可以設定對象在x、y和z軸的坐标。對象的位置是相對于它的父對象來說的,通常父對象就是添加該對象的場景,但有的時候可能是THREE.Object3D對象或其他THREE.Mesh對象。在第5章讨論對象組合時我們再來讨論這個問題。有三種方式用于設定對象的位置。第一種是直接設定坐标,代碼如下所示:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

也可以一次性地設定x、y和z坐标的值:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

還有第三種方式。position屬性是一個THREE.Vector3對象,這意味着我們可以像下面這樣設定該對象:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

效果圖如圖2.11所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

但是如果現在移動該對象組,會發現其位移值保持不變。在第5章中我們還會深入讨論這種父子關系,以及對象組是如何影響變換(如縮放、旋轉和平移)的。

下一個我們将要介紹的是rotation(旋轉)屬性。在本章和前一章中我們已經多次使用這個屬性了,通過這個屬性可以設定對象繞軸的旋轉弧度。我們可以像設定position屬性那樣來設定rotation屬性。在數學上物體旋轉一周的弧度值為2,是以可以用如下三種方式設定旋轉:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

如果想使用度數(0到360)來設定旋轉,那麼需要對度數做如下轉換:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

你可以通過示例06-mesh-properties.html來體驗該屬性。

屬性清單中還沒有讨論的屬性是scale(縮放)。從名字我們就已經知道該屬性是用來做什麼了。該屬性讓我們可以沿指定軸縮放對象。如果設定的縮放值小于1,那麼物體就會縮小,如圖2.12所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

如果設定的縮放值大于1,那麼物體就會變大,如圖2.13所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在本章中将要繼續讨論的是網格的translate()方法。使用translate()方法你可以改變對象的位置,但是該方法設定的不是物體的絕對位置,而是物體相對于目前位置的平移距離。假設在場景中有個球體,其位置為(1,2,3)。如果我們想讓這個對象相對于x軸平移4個機關:translateX(4),那麼物體的位置就會變為(5,2,3)。如果我們想重置物體的位置為原來的位置,可以調用translateX(-4)。在示例06-mesh-properties.html中有個translate菜單項,通過這個菜單項你可以體驗這個功能。隻要設定沿x、y和z軸方向的平移距離,然後點選translate按鈕,你就可以看到物體依照這三個值平移到一個新的位置。

最後一個可以使用的是菜單項右上角的visible屬性。當你點選visible菜單項時,會發現立方體消失了,如圖2.14所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

當你再次點選visible按鈕時,立方體又再次出現了。在第5章和第7章中,我們還會進一步介紹網格和幾何體,以及如何使用這些對象。

2.3 選擇合适的錄影機

Three.js庫提供了兩種不同的錄影機:正交投影錄影機和透視投影錄影機。在第3章中将會詳細介紹如何使用這些錄影機,是以在本章中隻會介紹一些比較基礎的内容。值得注意的是,Three.js還提供了一些非常特殊的錄影機用于支援3D眼鏡和VR裝置。由于它們與本章将要講述的基礎錄影機的工作方式類似,是以本書不會深入介紹這些特殊錄影機。

如果你隻需要簡單VR錄影機(即标準的立體視覺效果),可以使用THREE.StereoCamera将左右眼畫面并排渲染,或者也可以使用其他特殊錄影機渲染視差屏障式的(例如3DS提供的裝置)3D圖像,或者是傳統的紅藍重疊式的3D圖像。

此外,Three.js還實驗性地支援WebVR這一被浏覽器廣泛支援的标準(更多資訊請參考

https://webvr.

info/developers/ )。為了啟用這一支援,隻需要設定renderer.vr.enabled = true;,後續工作Three.js會為你做好。在Three.js的官方網站可以找到有關這一屬性值以及其他WebVR相關特性的示例:

https:/

/threejs.org/examples/。

下面将會使用幾個示例來解釋正交投影錄影機和透視投影錄影機的不同之處。

2.3.1 正交投影錄影機和透視投影錄影機

在本章的示例中有個名為07-both-cameras.html的例子。當你打開該示例時,會看到如圖2.15所示的結果。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

這就是透視視圖,也是最自然的視圖。正如你所看到的,這些立方體距離錄影機越遠,它們就會被渲染得越小。如果我們使用另外一種錄影機—正交投影錄影機,對于同一個場景你将會看到如圖2.16所示的效果。

使用正交投影錄影機的話,所有的立方體被渲染出來的尺寸都是一樣的,因為對象相對于錄影機的距離對渲染的結果是沒有影響的。這種錄影機通常被用于二維遊戲中,比如《模拟城市4》和早期版本的《文明》。如圖2.17所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在我們的示例中,大部分使用的是透視投影錄影機,因為這種錄影機的效果更貼近真實世界。在Three.js中改變錄影機是很簡單的,當你點選07-both-cameras.html示例中的switchCamera按鈕時,下面的代碼将會被執行:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章
帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

THREE.PerspectiveCamera和THREE.OrthographicCamera的建立方法是不一樣的。首先我們先來看下THREE.PerspectiveCamera,該方法接受的參數如表2.4所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

這些屬性結合到一起影響你所看到的景象,如圖2.18所示。

錄影機的fov屬性決定了橫向視場。基于aspect屬性,縱向視場也就相應地确定了。near屬性決定了近面距離,far屬性決定了遠面距離。近面距離和遠面距離之間的區域将會被渲染。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

如果要配置正交投影錄影機,我們需要使用其他的一些屬性。由于正交投影錄影機渲染出的物體大小都是一樣的,是以它并不關心使用什麼長寬比,或者以什麼樣的視角來觀察場景。當使用正交投影錄影機時,你要定義的是一個需要被渲染的方塊區域。表2.5列出了正交投影錄影機相應的屬性。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

所有這些屬性可以總結為圖2.19。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

2.3.2 将錄影機聚焦在指定點上

到目前為止,我們已經介紹了如何建立錄影機,以及各個參數的含義。在前面一章中,我們也講過錄影機需要放置在場景中的某個位置,以及錄影機能夠看到的區域将會被渲染。通常來說,錄影機會指向場景的中心,用坐标來表示就是position(0,0,0)。但是我們可以很容易地改變錄影機所指向的位置,代碼如下所示:

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

在我們的示例中,錄影機是可以移動的,而且它所指向的位置标記一個紅點,如圖2.20所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

如果你打開示例08-cameras-lookat.html,就會發現場景正從左向右移動。其實場景并沒有移動,而是錄影機從不同點(螢幕中央的紅點)拍攝場景,其帶來的效果就是場景從左向右移動。在該示例中,你還可以切換到正交投影錄影機;你會看到改變錄影機拍攝位置所帶來的效果和使用透視投影錄影機的效果是相同的。有意思的是,不管錄影機拍攝的位置如何改變,正交投影錄影機拍攝出來的所有立方體大小都是一樣的。如圖2.21所示。

帶你讀《Three. js開發指南: 基于WebGL和HTML5在網頁上渲染 3D圖形和動畫(原書第3版)》之二:建構Three.js應用的基本元件第2章

當使用lookAt()方法時,可以在某個特定的位置設定錄影機。使用該方法還可以讓錄影機追随場景中的某個物體。由于THREE.Mesh對象的位置都是THREE.Vector3對象,是以可以使用lookAt()方法使錄影機指向場景中特定的某個網格。你所需要做的就是輸入如下代碼:camera.lookAt(mesh.position)。如果在render循環中執行該代碼,你所看到的效果就是錄影機随着物體的移動而移動。

2.4 總結

在本章中我們介紹了THREE.Scene的所有屬性和方法,并解釋了如何使用這些屬性來配置主場景。我們還展示了如何使用THREE.Geometry對象或者使用Three.js内置的幾何體來建立幾何體。最後我們介紹了如何配置Three.js提供的兩種錄影機:透視投影錄影機使用接近真實世界的視角來渲染場景,而正交投影錄影機提供了一種在遊戲中被廣泛采用的僞三維效果。我們還介紹了Three.js中的幾何體是如何工作的。現在你就可以很簡單地建立你自己的幾何體了。

在下一章中,我們将會介紹Three.js庫提供的各種不同光源。你将會學到各種不同光源的行為,如何建立和配置這些光源,以及它們對特定材質的影響。

繼續閱讀