簡介
我曾在一些實驗中使用 Three.js,它能非常出色地抽象化瀏覽器中 3D 的使用難題。您可以使用此工具建立攝影機、物件、燈光、材質等,並選擇要使用的轉譯器,也就是決定是否要使用 HTML 5 的畫布、WebGL 或 SVG 繪製場景。由於這是開放原始碼,您甚至可以參與專案。不過,我現在會著重於透過將其當作引擎來玩耍,所學到的知識,並向您介紹一些基本概念。
雖然 Three.js 非常出色,但有時您可能會遇到困難。通常您需要花費大量時間研究範例、進行逆向工程,以及 (在我的情況中) 尋找特定功能,偶爾也會透過 GitHub 提出問題。順帶一提,如果您有任何問題,我發現 Mr. doob 和 AlteredQualia 非常實用!
1. 基本概念
我會假設您至少具備 3D 的基礎知識,並對 JavaScript 有一定程度的熟悉程度。如果您不熟悉這些概念,建議先學習相關知識,再嘗試使用這些工具,因為這些概念可能會讓您感到困惑。
在 3D 世界中,我們會使用以下一些元素,我將引導您完成建立程序:
- 場景
- 轉譯器
- 相機
- 一或兩個物件 (含材質)
當然,您也可以執行一些很酷的操作,我希望您能繼續這樣做,並開始在瀏覽器中嘗試 3D 功能。
2. 支援
關於瀏覽器支援功能,我有幾點要提醒你。就我個人經驗而言,Google Chrome 瀏覽器是最佳瀏覽器,可支援哪些轉譯器,以及底層 JavaScript 引擎的速度。Chrome 支援 Canvas、WebGL 和 SVG,速度極快。在 Firefox 推出第 4 版後,這項服務的使用率也緊追其後。它的 JavaScript 引擎確實比 Chrome 慢一點,但它支援的轉譯技術非常出色。Opera 和 Safari 正在新增 WebGL 支援功能,但目前版本僅支援畫布。Internet Explorer (9 以上版本) 僅支援畫布算繪,我並未聽說 Microsoft 有意新增 WebGL 功能。
3. 設定場景
我假設您已選擇支援所有算繪技術的瀏覽器,且想要使用畫布或 WebGL 進行算繪,因為這兩者是較標準的選擇。相較於 WebGL,Canvas 的支援範圍更廣泛,但值得注意的是,WebGL 會在顯示卡的 GPU 上執行,這表示 CPU 可以專注於其他非算繪工作,例如您要執行的任何物理或使用者互動。
無論您選擇哪種轉譯器,請務必記住,JavaScript 需要進行最佳化,才能提升效能。3D 並非瀏覽器的輕量工作 (而且能做到這一點實在太棒了),因此請仔細瞭解程式碼中是否有任何瓶頸,並盡可能移除這些瓶頸!
因此,假設您已下載並在 HTML 檔案中加入 three.js,那麼您要如何設定場景?如下所示:
// set the scene size
var WIDTH = 400,
HEIGHT = 300;
// set some camera attributes
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
// get the DOM element to attach to
// - assume we've got jQuery to hand
var $container = $('#container');
// create a WebGL renderer, camera
// and a scene
var renderer = new THREE.WebGLRenderer();
var camera = new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR );
var scene = new THREE.Scene();
// the camera starts at 0,0,0 so pull it back
camera.position.z = 300;
// start the renderer
renderer.setSize(WIDTH, HEIGHT);
// attach the render-supplied DOM element
$container.append(renderer.domElement);
其實一點都不難!
4. 製作網格
因此,我們有場景、攝影機和渲染器 (我在範例程式碼中選擇了 WebGL),但沒有實際繪製的內容。Three.js 其實支援載入幾種不同的標準檔案類型,如果您要從 Blender、Maya、Cinema 4D 或其他軟體輸出模型,這項功能就非常實用。為簡化流程 (畢竟這只是讓您開始使用),我會談論基本元素。基本元素是幾何圖形網格,例如球體、平面、立方體和圓柱等相對基本元素。Three.js 可讓您輕鬆建立下列原始類型:
// set up the sphere vars
var radius = 50, segments = 16, rings = 16;
// create a new mesh with sphere geometry -
// we will cover the sphereMaterial next!
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(radius,
segments,
rings),
sphereMaterial);
// add the sphere to the scene
scene.add(sphere);
沒問題,但球體的材質呢?在程式碼中,我們使用了變數 sphereMaterial,但尚未定義該變數。首先,我們需要進一步討論素材。
5. 材質
這無疑是 Three.js 最實用的部分之一。它提供多種常見 (且非常方便) 的材質,可套用至多邊形:
- 「Basic」:表示只會算繪「unlit」
- Lambert
- Phong
還有更多功能,但為了簡化說明,我會讓您自行探索。特別是在 WebGL 的情況下,這些素材可說是救星。這是因為因為在 WebGL 中,您必須為要算繪的所有內容編寫著色器。著色器本身就是一個龐大的主題,簡而言之,它們是以 GLSL (OpenGL 著色器語言) 編寫,可告知 GPU 應如何呈現內容。也就是說,您需要模擬燈光、反射等數學運算。這麼做可能會讓情況變得非常複雜。感謝 Three.js,您不必執行這項操作 (如果不想執行),因為 Three.js 會為您抽象化這項操作。不過,如果您想編寫著色器,也可以使用 MeshShaderMaterial 執行這項操作,因此設定方式相當靈活。
不過,我們先將 Lambert 材質套用至球體:
// create the sphere's material
var sphereMaterial = new THREE.MeshLambertMaterial(
{
// a gorgeous red.
color: 0xCC0000
});
值得一提的是,除了顏色之外,您在建立材質時也可以指定其他屬性,例如平滑或環境貼圖。您應該查看 Wiki 頁面,瞭解可在素材資源和引擎提供的任何物件上設定的各種屬性。此外,threejs.org 也於近期推出,提供更吸引人的 API 檢視畫面。
6. 燈光!
如果您現在算繪場景,就會看到紅色圓圈。雖然我們已套用 Lambert 材質,但場景中沒有光線,因此 Three.js 會預設為全環境光,這與平面著色相同。讓我們透過簡單的光點來解決這個問題:
// create a point light
var pointLight = new THREE.PointLight( 0xFFFFFF );
// set its position
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
// add to the scene
scene.add(pointLight);
7. 算繪
我們現在實際上已完成所有算繪設定,但我們實際上需要繼續執行以下操作:
// draw!
renderer.render(scene, camera);
不過,您可能會想多次轉譯,因此如果要執行迴圈,請務必使用 requestAnimationFrame,這是目前在瀏覽器中處理動畫的最佳方式。目前尚未全面支援,因此強烈建議您查看 Paul Irish 的 shim。
8. 常見物件屬性
仔細查看 Three.js 的程式碼,您會發現許多物件都「繼承」自 Object3D。這是一個基本物件,其中包含一些非常實用的屬性,例如位置、旋轉和縮放資訊。具體來說,我們的 Sphere 是繼承自 Object3D 的 Mesh,並新增了自己的屬性:幾何圖形和材質。為什麼我要提及這些?您可能不希望畫面上只有一個什麼事也不做的球體,因此這些屬性值得探討,因為這些屬性可讓您即時操控網格和材質的基礎細節。
// sphere geometry
sphere.geometry
// which contains the vertices and faces
sphere.geometry.vertices // an array
sphere.geometry.faces // also an array
// its position
sphere.position // has x, y and z properties
sphere.rotation // same
sphere.scale // ... same
9. Dirty Little Secrets
我只想快速指出 Three.js 的一個陷阱,也就是如果您修改網格頂點,您會發現在轉譯迴圈中沒有任何變化。這是因為因為 (據我所知) Three.js 會將網格資料快取為最佳化項目。您實際上需要做的是向 Three.js 標記某些內容已變更,以便重新計算所需的內容。您可以透過下列方式設定:
// changes to the vertices
sphere.geometry.__dirtyVertices = true;
// changes to the normals
sphere.geometry.__dirtyNormals = true;
當然,還有其他方法,但我發現這兩種方法最實用。您應該只標記已變更的項目,以免進行不必要的計算。
結論
希望這篇 Three.js 簡介對您有所幫助。沒有什麼比親自動手嘗試更能幫助您瞭解情況,我強烈建議您親自試試。在瀏覽器中原生執行 3D 內容非常有趣,使用 Three.js 等引擎可為您省去許多麻煩,讓您製作出非常酷炫的內容。為了協助您瞭解這項作業,我已在本研究室文章中提供原始碼,供您參考。如果你喜歡這篇文章,歡迎透過 Twitter 與我聯絡,我們很樂意與你交流!