小八——WebGL心路歷程(2),一個基於Three.js的webGL程式解析
在上一篇文章中簡單地介紹了一個原生態的webGL小程式,但是通過一個例子下來,我發現原生的api對於初學者的我來說有些吃力,特別是在頂點著色器和片段著色器這兩大神獸上,我糾結了十分長的時間。那麼,這個世界上總有那麼一些人,會為人類的進步無私地奉獻。在碼農界,這些大神會幫我們封裝好一些很複雜的操作,開源他們的實現程式碼,然後為大家所用。因為是跟著《webGL入門指南》這本書的節奏,所以我也跟著書中推薦的Three.js開始學習。這一節我就在這裡介紹介紹Three.js的一些基本內容。
在寫程式碼之前,我一般都會選一個特別好用的程式碼編輯器,有句話叫:工欲善其事,必先利其器。我在這一週多的時間選了很多編輯器,最讓我得心應手的,也就是我現在想要推薦的編輯器:webstorm(本人使用6.0.2版本,網上有很多破解的版本,主要用它來寫javascript的指令碼程式碼)以及sublime(神器不必多說,主要用它來寫glsl的程式碼片段),這兩把就是本人的刀和劍了。這兩個編輯器上手很快,強烈建議使用,當然如果有更好的,請一定告訴我!
另外,請先準備好Three.js的庫。
準備好一切以後,我們開始寫程式碼。閒話不說,看看我們第一個Three.js的例子
我這個人比較懶,這個例子來自《webGL入門指南》裡的2-1例子
<!DOCTYPE html>
<html>
<head>
<title>A Simple Three.js Page</title>
<scriptsrc="../libs/Three.js"></script>
<script>
function onLoad()
{
// Grab ourcontainer div
var container =document.getElementById("container");
// Create the Three.jsrenderer, add it to our div
var renderer = new THREE.WebGLRenderer();
renderer.setSize(container.offsetWidth,container.offsetHeight);
container.appendChild( renderer.domElement);
// Create a new Three.js scene
var scene = new THREE.Scene();
// Create a camera and add it to the scene
var camera = newTHREE.PerspectiveCamera( 45, container.offsetWidth / container.offsetHeight, 1,4000 );
camera.position.set(0, 0, 3.3333 );
scene.add( camera );
// Now, create arectangle and add it to the scene
var geometry = newTHREE.PlaneGeometry(1, 1);
var mesh = newTHREE.Mesh( geometry, new THREE.MeshBasicMaterial( ) );
scene.add( mesh );
// Render it
renderer.render( scene, camera );
}
</script>
</head>
<body onLoad="onLoad();">
<divid="container" style="width:500px; height:500px;background-color:#000000"></div>
</body>
</html>
執行結果如下:
使用Three.js這個框架以後,一下子就把我們程式碼量減少了許多。不僅僅如此,在對程式碼的理解上,我們也會更多地關注邏輯層的實現,而不用花太多的心思關注底層的很多東西。那麼,讓我們就像上一節課那樣把這個網頁小例子拿來一步步解析:
Html部分的程式碼就不做太多的解釋了,一個自帶id的div
在js程式碼部分如同註釋的片段的順序,我們將js的程式碼分為下面這幾個部分:
1. 獲取div,並初始化一個renderer的渲染器,並將渲染器新增到div標籤的子物件中。對應的程式碼:
var container =document.getElementById("container");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(container.offsetWidth,container.offsetHeight);
container.appendChild( renderer.domElement );
這裡給我的感覺就像是得到了HTML網頁上的一張畫布一樣,我們之後所有的渲染和動畫的過程都會在這個畫布上進行。這裡不用太鑽牛角尖,在實際使用過程,利用copy+paste改id的方法直接就可以把這段程式碼拿來用了。
2. 如果我們將我們要建立的webGL應用比喻成一部電影的話,第一步做的就是準備好場地,那麼第二步做的是初始化一個場景(在這個場景裡面,需要什麼演員,需要什麼樣的機位,需要做什麼事情),所對應的程式碼如下:
var scene = newTHREE.Scene();
3. 有場地,有場景,我們還需要一部相機,不需要陳老師,因為我們寫程式的就是陳老師,那麼下一步就是初始化一部相機,對應的程式碼如下:
var camera = new THREE.PerspectiveCamera(45,container.offsetWidth / container.offsetHeight, 1, 4000 );
camera.position.set( 0, 0,3.3333 );
scene.add( camera );
我們來看看初始化相機的程式碼,PerspectiveCamera這個api的引數和opengl中的gluPerspective的api其實是一一對應的第一個引數表示視野角度(把相機看成自己的眼睛的話,其實感覺就是眼睛睜開的角度),第二個引數是長寬比(理解為相片的長寬比),第三個引數和第四個引數則是近側裁剪平面和遠側裁剪平面相對於相機來說的距離。如果需要深刻理解這幾個引數的話,請自行百度透視投影以及opengl中gluPerspective函式就能理解更透徹了。
而後設定相機的位置,最後向我們的場景中新增相機。
4. 我們一切都準備好了,還差演員。對於這個場景來說,演員就是那塊正方形。定義一個演員在Three.js中需要有三個步驟:①定義它的幾何引數;②定義它的材質;③將其放入一個網格當中。對應這三個步驟的程式碼就是:
var geometry = new THREE.PlaneGeometry(1,1);
var mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial( ) );
程式碼中將②③兩步和在了一起,至於PlaneGeometry,只是Three.js事先為我們寫好的有關一個矩形的實現方法,另外相關的材質MeshBasicMaterial大可不必太理會,如果要深究,可檢視Three.js提供的api文件選擇自己喜歡的材質。注意:幾何體定義好幾何引數以及材質後一定要將其放入網格中,不然無法在場景中顯示。
這個演員我們造好了,下一步我們就把它放入我們的場景中
scene.add(mesh);
5. 最後一步我們用渲染renderer這個物件的render函式渲染div
renderer.render(scene,camera);
這個程式對於我們來說,是不是比上面一個例子更好理解?在此感謝為我們學習打好了堅實基礎的大神mrdoob,是他編寫了Three.js這樣優雅簡單易懂的框架。
延伸的問題:(請在理解上面的程式碼後檢視)
在這裡不禁有個問題了,在很多時候我們自己寫個webGL的時候很多程式碼都是需要重複寫的,我們可不可以自己寫一個自己的框架,每次編寫的時候只需要呼叫我們自己的函式就能避免這些重複複雜無謂的操作呢?答案是yes
具體的工作可以根據個人需要自己寫寫看框架。