WebGL - 頂點數據矩陣變換

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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章