使用 glMatrix


在先前文件中,我們自行推導出各種矩陣,並實作為各個函式,有能力或有時間的話,這是值得推薦的過程,不但可以理解各種轉換背後的原理,在呼叫各個函式時,對於每個參數的作用也會更加明瞭,知道在什麼情況下該採用什麼呼叫。

當然,每個矩陣或函式都要推導與實作,也是蠻累人的一件事,有沒有程式庫來幫忙做這件事呢?可以使用 glMatrix,實際上先前在實作各個矩陣相關函式時,我也是參考 glMatrix 的原始碼,以避免實作上一些失誤或沒有注意到的地方。

因為是參考 glMatrix 實作的,如果你也跟隨著先前文件的內容,在接觸 glMatrix 時應該是沒太大問題,在撰寫本文時,glMatrix 版本是 2.0,src 目錄中的原始碼是基於 ES6 實作,使用 ES6 模組功能來管理不同的模組原始碼,如果偏好使用 ES6 模組,可以直接使用這些模組,例如,先前文件在實作時,主要是參考 src/mat4.js 的內容。

如果在意相容性,可以在 dist 中找到 gl-matrix.js,這是透過建構工具建構後,不使用 ES6 相關語法的版本,全域變數基本上就是 src 中看到的各個模組名稱,這在 gl-matrix.js 原始碼最下方中可以看到:

exports.glMatrix = common;
exports.mat2 = mat2;
exports.mat2d = mat2d;
exports.mat3 = mat3;
exports.mat4 = mat4;
exports.quat = quat;
exports.quat2 = quat2;
exports.vec2 = vec2;
exports.vec3 = vec3;
exports.vec4 = vec4;

gl-matrix-min.js 是 gl-matrix.js 的壓縮版本,實際上線後的頁面可以使用這個版本。

如果你想要自行建構 gl-matrix.js、gl-matrix-min.js,可以安裝 Node.js,將 glMatrixgit clone 後,進入目錄並執行:

npm install
npm run build

建構完成之後,同樣是在 dist 目錄中會出現 gl-matrix.js、gl-matrix-min.js。

如果要查詢 API 文件,可以查看 Documentation,基本上就是將原始碼中註解文件的部份用 JSDoc 網頁化,我是都直接看原始碼中的註解。

glMatrix 採用 OpenGL/WebGL 的慣例,以線性陣列來實作矩陣時,都是採取行為主(column-major),也就是對於矩陣:

1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 0

使用陣列實作時要寫成:

[
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    x, y, z, 0
]

glMatrix 可用來建立新矩陣的相關函式,傳回值大多是 Float32Array 而不是 Array,至於與矩陣運算相關的函式,參數部份可以接受 ArrayFloat32Array 或 Array-like 實例,因為原始碼實作中都是基於索引來取元素值。

不過建議還是使用 Float32Array,這是因為與矩陣運算相關的函式,矩陣運算的結果慣例上,會儲存在第一個參數傳入的矩陣,這個矩陣也會被當成傳回值,如果第一個參數使用 Array,傳回值也會是 Array 型態,這也許不會是你想要的結果。

例如網頁中如果撰寫:

<script type="module">
    import * as mat4 from './mat4.js';

    const m = mat4.create();
    console.log(m);

    mat4.translate(m, m, [10, 10, 10]);
    console.log(m);  // 可以看到 m 內容改變了
</script>

mat4.create() 會建立 4x4 單位矩陣,也就是傳回:

[
    1, 0, 0, 0, 
    0, 1, 0, 0, 
    0, 0, 1, 0, 
    0, 0, 0, 1
]

mat4.translate(m, m, [10, 10, 10]) 的第一個參數會用來儲存位移操作後的結果,第二個參數是要參與位移運算的矩陣,就上例來說,m 的內容會被改變,也就是 m 結果會是:

[
    1, 0, 0, 0, 
    0, 1, 0, 0, 
    0, 0, 1, 0, 
    10, 10, 10, 1
]

如果不想改變 m,就是在第一個參數指定另一個矩陣,例如:

<script type="module">
    import * as mat4 from './mat4.js';

    const m = mat4.create();
    console.log(m);

    const r = mat4.translate(mat4.create(), m, [10, 10, 10]);
    console.log(m);  // 可以看到 m 內容沒有改變
    console.log(r);  // 矩陣運算後的結果
</script>