[WebGL] three.js 學習筆記 #1

前陣子在找四元樹與八元樹的資料,發現了 three.js 這個套件,被用於做四元樹相關演算法的圖像展示。

three.js 是一個重新封裝 WebGL 語法的一個 javascript library,大幅降低開發 WebGL 的難度,之前有簡單接觸過 OpenGL,這次趁著週末便來挑戰看看 WebGL 吧!

WebGL 與 OpenGL 的比較感想

跨平台支援性高

因為是用網頁呈現,所以只要瀏覽器有支援 WebGL,再配合文字編輯器,不需特別準備編譯環境便可以開發。

仍在維護的套件可以幫助開發

目前夠透過 three.js 可以建立配置 scene 跟 mesh,比起當初用 C 語言開發 OpenGL 要手動處理頂點資料,要方便很多。
OpenGL 只有找到處理數學或讀取模型的第三方 library,但沒有發現管理場景的工具可以利用。

網路資源多但凌亂,要注意 three.js 版本

這點跟 OpenGL 的情況類似,網路上要小心找到過舊的資料。three.js 雖為 open source,有 bug 通常不會無法解決,但更有版本演進快速,老舊語法快速被淘汰,網路資料沒有更新的問題。

開發筆記

程式碼基本模板

javascript 可以直接嵌入 html,並且直接透過瀏覽器觀看結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<body>
<script>
var renderer, scene, camera;

init();
animate();

function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);

// add objects to scene

renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}

function animate() {
requestAnimationFrame(animate);
// update scene ...
renderer.render(scene, camera);
}
</script>
</body>

合併 Mesh

three.js 內建了許多 Primitive Geometry ,稍微設定數個簡單幾何的位置角度,再加以合併,就可以拼裝成簡單的測試場景。
合併 mesh 的方法如下:

1
2
3
4
5
6
7
var masterMesh = new THREE.Mesh();

var plane = new THREE.PlaneGeometry(5, 5, 4, 4);
var planeMesh = new THREE.Mesh(plane);
planeMesh.updateMatrix();

masterMesh.merge(planeMesh.geometry, planeMesh.matrix);

Custom shader material

也可以直接在 html 中嵌入一段字串作為 vertex shader 或 fragment shader 原始碼,在後續讀取為自訂的材質來使用。

1
2
3
4
5
6
<script type="x-shader/x-vertex" id="vertexShader">
// shader code
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
// shader code
</script>

讀取 shader program 的基本方式:

1
2
3
4
5
6
7
8
9
10
11
12
var vertexShader = document.getElementById('vertexShader').textContent;
var fragmentShader = document.getElementById('fragmentShader').textContent;
var uniforms = {
// ...
}

var shaderMat = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
side: THREE.FrontSide
});

shader programing 內建變數

three.js 的 ShaderMaterial 會內建一些變數到 shader 之中,可以直接在撰寫時調用:

傳遞 uniform

與開發 OpenGL 時一樣,我們需要頻繁地寫入 shader 的 Uniform variable 作為畫面更新的手段,three.js 也提供了相關辦法,並且會在資料有變動時自動更新到 shader 之中,相當方便

遇到的小困擾

我們時常會將 shader 所需的資料打包成 struct,此時 Uniform variable 的傳遞必須使用 array 才能正常運作,不知是否為 bug。

這裡是 github 上相關的討論,我自己次是的結果是 struct 中的 vec3 等向量變數會不正常,改用 array 是個暫時的解決手段。

幾個小 Demo

借用 twitter 來上傳我做的幾個小測試,分別為:

拼裝場景,加上內建材質

利用 build-in variable 自製 Normal 向量的展示材質

進一步自行撰寫一個 Directional Light 的材質