WebGL之旅(五)組合變換

複合變換,即對一個圖形進行多次變換。

一 組合原理

比如,對圖像g進行一次平移,然後進行一次縮放,求最終的得到圖像g。

那麼:

平移後的座標t = 平移矩陣 x 原始座標g
縮放後的G = 縮放矩陣 x 平移後的座標t

所以:

縮放後的G = 縮放矩陣 x (平移矩陣 x 原始座標g) = (縮放矩陣 x 平移矩陣) x 原始座標g

可見,對於多次變換,可以將變換矩陣相乘得到一個最終的矩陣(叫模型矩陣),然後傳給頂點着色器做最終的運算。

示例

/**
 * 複合變換
 * [email protected]
 * */

// 頂點着色器源碼
var vertexShaderSrc = `
attribute vec4 a_Position;// 接收傳入位置座標,必須聲明爲全局
uniform mat4 u_Mat;// 旋轉矩陣
void main(){
    gl_Position = u_Mat * a_Position;
}`;

// 片段着色器源碼
var fragmentShaderSrc = `
void main(){
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);// gl_FragColor 內置變量,表示片元顏色,必須賦值
}`;

// 初始化使用的shader
function initShader(gl) {
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);// 創建頂點着色器
    gl.shaderSource(vertexShader, vertexShaderSrc);// 綁定頂點着色器源碼
    gl.compileShader(vertexShader);// 編譯定點着色器

    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);// 創建片段着色器
    gl.shaderSource(fragmentShader, fragmentShaderSrc);// 綁定片段着色器源碼
    gl.compileShader(fragmentShader);// 編譯片段着色器

    var shaderProgram = gl.createProgram();// 創建着色器程序
    gl.attachShader(shaderProgram, vertexShader);// 指定頂點着色器
    gl.attachShader(shaderProgram, fragmentShader);// 指定片段着色色器
    gl.linkProgram(shaderProgram);// 鏈接程序
    gl.useProgram(shaderProgram);//使用着色器

    gl.program = shaderProgram;
}

function main() {
    var canvas = document.getElementById("container");
    var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    initShader(gl);// 初始化着色器
    var n = initVertexBuffers(gl);// 初始化頂點

    // 平移矩陣,列主序
    var tran = getTranslationMatrix(0.5, 0.5, 0.0);

    // 旋轉矩陣,列主序
    var rot = getRotationMatrix(Math.PI * 0.5, 0.0, 0.0, 1.0);

    // 縮放矩陣,列主序
    var scale = getScaleMatrix(0.5, 0.5, 1.0);

    var mat = multiMatrix44(scale, tran);
    mat = multiMatrix44(mat, rot);
    var u_Mat = gl.getUniformLocation(gl.program, 'u_Mat');
    gl.uniformMatrix4fv(u_Mat, false, mat);

    gl.clearColor(0.0, 0.0, 0.0, 1.0);// 指定清空canvas的顏色
    gl.clear(gl.COLOR_BUFFER_BIT);// 清空canvas
    gl.drawArrays(gl.TRIANGLES, 0, n);
}

function initVertexBuffers(gl) {
    var vertices = new Float32Array([
        0, 0.5,
        -0.5, -0.5,
        0.5, -0.5,
    ]);

    var vertexBuffer = gl.createBuffer();// 創建緩衝區對象
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);// 將緩衝區對象綁定到目標
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 向緩衝區中寫入數據

    var a_Position = gl.getAttribLocation(gl.program, "a_Position");// 獲取a_Position變量
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);// 將緩衝區對象分配給a_Position
    gl.enableVertexAttribArray(a_Position);// 鏈接a_Position與分配給他的緩衝區對象

    return vertices.length / 2;
}

/**
 * 由平移向量獲取平移矩陣
 * */
function getTranslationMatrix(x, y, z) {
    return new Float32Array([
        1.0, 0.0, 0.0, 0.0,
        0.0, 1.0, 0.0, 0.0,
        0.0, 0.0, 1.0, 0.0,
        x, y, z, 1.0,
    ]);
}

/**
 * 由旋轉弧度和旋轉軸獲取旋轉矩陣
 * */
function getRotationMatrix(rad, x, y, z) {
    if (x > 0) {
        // 繞x軸的旋轉矩陣
        return new Float32Array([
            1.0, 0.0, 0.0, 0.0,
            0.0, Math.cos(rad), -Math.sin(rad), 0.0,
            0.0, Math.sin(rad), Math.cos(rad), 0.0,
            0.0, 0.0, 0.0, 1.0,
        ]);
    } else if (y > 0) {
        // 繞y軸的旋轉矩陣
        return new Float32Array([
            Math.cos(rad), 0.0, -Math.sin(rad), 0.0,
            0.0, 1.0, 0.0, 0.0,
            Math.sin(rad), 0.0, Math.cos(rad), 0.0,
            0.0, 0.0, 0.0, 1.0,
        ]);
    } else if(z > 0) {
        // 繞z軸的旋轉矩陣
        return new Float32Array([
            Math.cos(rad), Math.sin(rad), 0.0, 0.0,
            -Math.sin(rad), Math.cos(rad), 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0,
        ]);
    } else {
        // 沒有指定旋轉軸,報個錯,返回一個單位矩陣
        console.error("error: no axis");
        return new Float32Array([
            1.0, 0.0, 0.0, 0.0,
            0.0, 1.0, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0,
        ]);
    }
}

/**
 * 由縮放因子獲取縮放矩陣
 * */
function getScaleMatrix(xScale, yScale, zScale) {
    return new Float32Array([
        xScale, 0.0, 0.0, 0.0,
        0.0, yScale, 0.0, 0.0,
        0.0, 0.0, zScale, 0.0,
        0.0, 0.0, 0.0, 1.0,
    ]);
}

/**
 * 1 x 1 向量點乘
 * */
function dotMultiVector(v1, v2) {
    var res = 0;
    for (var i = 0; i < v1.length; i++) {
        res += v1[i] * v2[i];
    }
    return res;
}

/**
 * 4 x 4 矩陣的轉置
 * */
function transposeMatrix(mat) {
    var res = new Float32Array(16);
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            res[i * 4 + j] = mat[j * 4 + i];
        }
    }
    return res;
}

/**
 * 4 x 4 矩陣乘法
 * */
function multiMatrix44(m1, m2) {
    var mat1 = transposeMatrix(m1);
    var mat2 = transposeMatrix(m2);

    var res = new Float32Array(16);
    for (var i = 0; i < 4; i++) {
        var row = [mat1[i * 4], mat1[i * 4 + 1], mat1[i * 4 + 2], mat1[i * 4 + 3]];
        for (var j = 0; j < 4; j++) {
            var col = [mat2[j], mat2[j + 4], mat2[j + 8], mat2[j + 12]];
            res[i * 4 + j] = dotMultiVector(row, col);
        }
    }
    return transposeMatrix(res);
}

如圖:

這裏寫圖片描述

這裏主要涉及到一個向量乘法的運算transposeMatrix。

因爲是用列主序表示的,所以想把列主序轉轉置成行主序,然後在計算,計算完之後再轉成列主序返回。

發佈了364 篇原創文章 · 獲贊 148 · 訪問量 89萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章