譯序
Three.js是一個偉大的開源WebGL庫,WebGL允許JavaScript操作GPU,在瀏覽器端實現真正意義的3D。但是目前這項技術還處在發展階段,資料極為匱乏,愛好者學習基本要通過Demo源碼和Three.js本身的源碼來學習。
國外網站 aerotwist.com 有六篇較為簡單的入門教程,我嘗試着將其翻譯過來,與大家分享。
我在一些實驗項目中使用了Three.js,我發現它對快速上手瀏覽器3D編程確實很有幫助。通過Three.js,你不僅可以創建相機、物體、光線、材質等等,還可以選擇着色器,可以決定使用何種技術(WebGL、Canvas或SVG)在網頁上渲染你的3D圖形。Three.js是開源的,你甚至可以參與到這個項目中來。但現在,我將把重點放在基礎的介紹上,我將向你展示如何使用這個引擎上。
盡管Three.js如此奇妙,但有時候它也會令人抓狂。比如,你將花費大量時間閱讀例程,做一些逆向工程(在我的情形下)來確定某個函數的作用,有時還要去GitHub上提問。如果你需要提問,Mr. doob和AlteredQualia是極好的選擇。
1.基礎
我假定你的三維圖形學知識過關,而且也在一定程度上掌握了JavaScript。如果不是這樣,那先去學一點吧,否則直接看這篇教程,也許會感到困惑。
在我們的三維世界里,我們有以下這些東西。我會帶你一步一步創建它們。
- 場景
- 渲染器
- 相機
- 物體(帶有材質的)
當然,你也可以創造些其他的什么東西,我也希望你如此做。
2.瀏覽器支持
簡單地看一下瀏覽器的支持情況吧。Google家的Chrome瀏覽器支持Three.js,在我的實驗里,無論是對渲染器的支持程度還是JavaScript解釋器的運行速度,Chrome都是做得最好的:它支持Canvas、WebGL和SVG,而且運行得非常快。FireFox瀏覽器排在第二位,它的JavaScript引擎的速度比Chrome慢了半拍,但是對渲染器的支持也很棒,而且FireFox的速度,隨着版本更新也越來越快。Opera瀏覽器正在逐漸增加對WebGL的支持,Mac上的Safari瀏覽器有一個開啟WebGL的選項。總體上,這兩個瀏覽器僅僅支持Canvas渲染。微軟家的IE9現在只支持Canvas渲染,而且微軟似乎並不樂意支持WebGL這個新特性,所以我們現在肯定不會用IE9來做實驗。
3.設置場景
假定你已經選擇了一個支持所有渲染技術的瀏覽器,而且你准備通過Canvas或WebGL來渲染場景(這是更標准化的選擇)。Canvas比WebGL有着更廣泛地支持,但是WebGL可以直接在GPU上操作,這意味着你的CPU可以專注地處理非渲染類的工作,比如物理引擎或與用戶交互等。
無論你選擇何種渲染器,你都必須牢記在心的是:JavaScript代碼需要優化。三維顯示對瀏覽器來說不是一項輕松的工作(現在能夠這樣做就很偉大了),所以如果你的渲染太慢了,你需要知道你代碼的瓶頸在何處,如果可能,改善它。
說了這么多,我想你已經下載好Three.js源代碼,而且將它引入了你的html文檔了。那么如何開始創建一個場景呢?就像這樣:
// 設置場景大小 var WIDTH = 400, HEIGHT = 300; // 設置一些相機參數 var VIEW_ANGLE = 45, ASPECT = WIDTH / HEIGHT, NEAR = 0.1, FAR = 10000; // 獲取DOM結構中的元素 // - 假設我們使用了JQuery var $container = $('#container'); // 創建渲染器、相機和場景 var renderer = new THREE.WebGLRenderer(); var camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR); var scene = new THREE.Scene(); // 將相機加入場景 scene.add(camera); // 相機的初始位置為原點 // 將相機拉回來一些(譯者注:這樣才能看到原點) camera.position.z = 300; // 啟動渲染器 renderer.setSize(WIDTH, HEIGHT); // 將渲染器加到DOM結構中 $container.append(renderer.domElement);
你看,簡單吧!
4.構建網格表面
現在我們有了一個場景,一個相機和一個渲染器(在我的例子里,當然是一個WebGL渲染器),但我們事實上什么還沒畫呢。事實上,Three.js提供了載入某幾種標准格式3D文件的支持,如果你在Blender,Maya,Cinema4D或是什么其他工具中建模,這簡直太棒了。為了簡單(畢竟這才剛開始呢!)我們先來考慮基元。基元就是基本的幾何表面,比如最基本的球體、平面、立方體、圓柱體。利用Three.js可以很方便地創建這些基元:
// 設置球體參數(譯者注:球體被划分為16×16的網格,如果后兩個參數取4、2,則生成一個八面體,請想象) var radius = 50, segments = 16, rings = 16; // material覆蓋在geometry上,生成mesh var sphere = new THREE.Mesh( new THREE.SphereGeometry( radius, segments, rings), sphereMaterial); // 將mesh加入到場景中 scene.add(sphere);
好了,但是球體上的材質呢?在代碼中我們使用了一個sphereMaterial變量,我們還沒定義它呢。那我們就先來看看怎么創建材質吧。
5.材質
毫無疑問,這是Three.js最有用的部分了。這部分提供了幾個非常易用的通用材質模型:
- Basic材質:表示一種不考慮光照的材質,現在只能這么說了。
- Lambert材質:(譯者注:朗伯面,各向同性反射)。
- Phong材質:(譯者注:馮氏面,有光澤的表面,介於鏡面反射和朗伯反射之間的反射,描述真實世界的反射)。
除此之外,還有一些其他類型材質,簡單起見,就留給你自己探索。事實上,在使用WebGL類型的渲染器時,材質實在太好用了。為什么呢?因為在原生WebGL種你必須親自為每個渲染編寫着色器,而着色器本身就是個巨大的工程:簡單地說着色器是使用GLSL語言(OpenGL的着色器語言)寫的,用來操作GPU的程序,這意味着你要在數學上模擬光照,反射等等,這很快就變成一項極為復雜的工作。多虧有了Three.js你才可以不必去自己編寫着色器,當然,如果你想親自編寫的話,你可以使用MeshShaderMaterial,可見這是很靈活的設定。
現在,讓我們用朗伯面材質覆蓋球體:
// 創建球體表面的材質 var sphereMaterial = new THREE.MeshLambertMaterial( { color: 0xCC0000 });
值得指出的是,創建材質的時候,除了顏色還有很多其他參數可以指定,比如光滑度和環境貼圖。你可以需要檢索這個Wiki頁面來確認哪些是哪些屬性可以設置在材質上,或Three.js引擎提供的任何對象上。
6.光!
如果你現在就想渲染場景,你會看到一個紅色的圓。雖然我們在球體上覆蓋了朗伯面材質,但場景里沒有光。所以按照默認設定,Three.js會恢復到滿環境光,物體的看上去的顏色就是物體表面的顏色。讓我們添加一個簡單的點光源:
// 創建一個點光源 var pointLight = new THREE.PointLight(0xFFFFFF); // 設置點光源的位置 pointLight.position.x = 10; pointLight.position.y = 50; pointLight.position.z = 130; // 將點光源加入場景 scene.add(pointLight);
7.渲染循環
顯然,關於渲染器的一切都設置好了。萬事俱備,我們現在只需要:
// 畫! renderer.render(scene, camera);
你很可能像多次渲染,而不是只渲染一次,所以如果你要去做一個循環,你應該使用requestAnimationFrame。這是目前最好的,在瀏覽器中處理動畫的方法,雖然還沒有得到最全面的支持,但我強烈建議你去看一看Paul Irish的博客。
8.通用的對象屬性
如果你花點時間去瀏覽一遍Three.js的源代碼,你會發現很多對象都繼承自Object3D。這個基類包含了很多有用的屬性,比如位置、旋轉和縮放的信息。特別的,我們的球體是一個Mesh對象,而Mesh對象繼承自Object3D對象,但是又增加了些自己的屬性:geometry和material。為什么要說這些?因為你一定不會只滿足於屏幕中一個什么都不做的圓球,而這些(譯者注:基類中的)屬性允許你操作Mesh對象更底層的細節和各種各樣的材質。
// sphere是一個mesh對象 sphere.geometry // sphere包含了一些點和面的信息 sphere.geometry.vertices // 一個數組 sphere.geometry.faces // 另一個數組 // mesh對象繼承自object3d對象 sphere.position // 包含x,y,z sphere.rotation // 同上 sphere.scale // ... 同上
9.討厭的秘密
我希望這樣說你能很快弄明白:就是如果你修改了,比如說,一個mesh對象的頂點屬性vertices,你會發現在渲染循環中,什么都沒變。為什么?因為Three.js將mesh對象的信息緩存為某種優化結構了。你真正要做的是給Three.js一個標識,告訴它如果什么東西改變了,需要重新計算緩存中的結構:
// 設置geometry為動態的,這樣才允許改變其中的頂點 sphere.geometry.dynamic = true; // 告訴Three.js,需要重新計算頂點 sphere.geometry.__dirtyVertices = true; // 告訴Three.js,需要重新計算頂點 sphere.geometry.__dirtyNormals = true;
還有更多的標識,但我發現這兩個是最有用的。你應該僅僅標識那些確實需要實時計算的屬性來避免無謂的運算開銷。
10.小結
我希望這篇簡單的介紹對你有所幫助。沒什么能比得上卷起袖子親手實踐了,我強烈建議你這樣做。在瀏覽器里面運行3D程序很有意思,而且使用像Three.js這樣一個引擎免去了很多麻煩,讓你一開始就能專注於那些真正cool的事情。
我將這篇教程的源碼打包了,你可以下載下來作為一份參考。
如果你喜歡Three.js,可以通過聯系板(譯者注:原文博客網站的聯系板)或者Twitter聯系我,有朋自遠方來,不亦樂乎!