1、Matrix4函數庫
此處需要了解一個矩陣函數庫cuon-matrix.js
這是 《webgl編程指南》
作者寫的一個庫,它封裝了一些簡單易用的方法,來實現一些複雜繁瑣的矩陣計算操作;
代碼地址:
https://gitee.com/ithanmang/webgl-notes
具體方法如下表:
Matrix4
對象有兩種方法,一種是名稱中帶有set
和一種不帶set
的;
- 包含
set
的 – 會根據參數計算出 變換矩陣,然後將變換矩陣寫入到 自身中; - 不含
set
的 – 會現根據參數計算出變換矩陣,然後將自身與剛剛計算得到的變換矩陣相乘;
2、基本變換
使用上面的方法可以方便進行矩陣變換:
如平移操作
var xformMatrix = new Matrix4();
// 平移
xformMatrix.setTranslate(0.5, 0.5, 0.5);
// 將變換後的矩陣傳遞 其中的 elements 是列主序的類型化數組
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix.elements);
頂點着色器接收就可以了
attribute vec4 a_Position;
uniform mat4 u_xformMatrix;
void main(){
// 變換後的座標 = 變換矩陣 * 原始座標
gl_Position = u_xformMatrix * a_Position;
}
3、複合變換
例如我們想要先平移然後再旋轉:
顯然,這包括了兩種變換,先進行平移變換然後再進行旋轉變換;
首先,先進行平移變換
- <平移後的座標> = <平移矩陣> * <原始座標>
然後,平移後的座標再進行旋轉變換
- <平移旋轉後的座標> = <旋轉矩陣> * <平移後的座標>
組合起來就是
- <平移旋轉後的座標> = <旋轉矩陣> * ( <平移矩陣> * <原始座標>)
所以可以先計算<旋轉矩陣> * <平移矩陣>
,然後將多次變換後的矩陣乘以 原始座標就可以實現複合變換;
一個模型可能經過了多次變換,將這些變換全部複合成一個等效的變換,也就得到了 模型變換,或稱 建模變換,所以,模型變換的矩陣稱爲 模型矩陣
所以此時着色器可以這樣寫
attribute vec4 a_Position;
// 聲明一個模型矩陣
uniform mat4 u_ModelMatrix;
void main(){
// 複合變換後的座標 = 模型矩陣 * 原始座標
gl_Position = u_ModelMatrix * a_Position;
}
使用Matrix4.js
中的方法可以這樣寫,先平移然後再旋轉
- 先平移:
xformMatrix.setTranslate(0.5, 0.5, 0)
- 再旋轉:
xformMatrix.rotate(45, 0, 0, 1)
先旋轉再平移,和先平移再旋轉計算的模型矩陣不一定相同
實例代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>先平移後旋轉</title>
<link rel="stylesheet" href="../css/common.css">
</head>
<body>
<canvas id="webgl" width="512" height="512"></canvas>
</body>
<script src="../lib/webgl-utils.js"></script>
<script src="../lib/webgl-debug.js"></script>
<script src="../lib/cuon-utils.js"></script>
<script src="../lib/cuon-matrix.js"></script>
<script>
// 頂點着色器
var vertex_shader_source = '' +
'attribute vec4 a_Position;' +
'uniform mat4 u_ModelMatrix;' +
'void main() {' +
' gl_Position = u_ModelMatrix * a_Position;' +
'}';
// 片元着色器
var fragment_shader_source = '' +
'void main(){' +
' gl_FragColor = vec4(0.5, 0.0, 0.5, 1.0);' +
'}';
(function () {
// 獲取canvas對象
var canvas = document.getElementById('webgl');
// 獲取webgl 上下文對象
var gl = getWebGLContext(canvas);
// 初始化着色器
if (!initShaders(gl, vertex_shader_source, fragment_shader_source)) {
console.log('初始化着色器失敗!');
return false;
}
// 設置頂點位置
var n = initVertexBuffer(gl);
if (n < 0) {
console.log('頂點寫入緩存失敗!');
return false;
}
// 旋轉角度
var ANGLE = 90.0;
// 創建旋轉矩陣對象
var modelMatrix = new Matrix4();
modelMatrix.setRotate(ANGLE, 0, 0, 1);
modelMatrix.translate(0.5, 0.5, 0);
// 通過旋轉矩陣傳遞給頂點着色器
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
if (!u_ModelMatrix) {
console.log('獲取uniform變量失敗!');
return false;
}
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
// 設置清空顏色
gl.clearColor(0.0, 0.5, 0.5, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n);
// 將頂點信息寫入緩存區
function initVertexBuffer(gl) {
var vertices = new Float32Array([
0.0, 0.25, 0.25, -0.25, -0.25, -0.25
]);
var n = vertices.length / 2;
// 創建緩衝區對象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('創建緩衝區對象失敗!');
return -1;
}
// 綁定緩衝區對象到目標
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 將數據寫入到緩衝區對象
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('獲取attribute變量失敗!');
return -1;
}
// 將緩衝區對象分配給attribute變量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 開啓attribute變量
gl.enableVertexAttribArray(a_Position);
return n;
}
}());
</script>
</html>