glMatrix 觀察、投影矩陣


在建立觀察矩陣方面,glMatrix 的 mat4 提供了 lookAt 函式,第一個參數是輸出矩陣,也會是函式的傳回值,之後的參數分別為 eyecenterup,分別為觀察者位置、看向的中心、頭頂方向的向量,因此,要建立一個新的觀察矩陣可以如下:

let lookTrans = mat4.lookAt(
    mat4.create(), 
    [0, viewy, 0], 
    [0, 0, 1], 
    [0, 1, 0]
);

例如,可以很快地將〈觀察矩陣〉中的範例改寫為使用 glMatrix,與矩陣操作相關的部份如下:

let viewy = 0.5;
let lookTrans = mat4.lookAt(mat4.create(), [0, viewy, 0], [0, 0, 1], [0, 1, 0]);

canvas.addEventListener('mousedown', () => {
    viewy *= -1;
    mat4.identity(lookTrans);
    mat4.lookAt(lookTrans, [0, viewy, 0], [0, 0, 1], [0, 1, 0]);
});

let transformation = mat4.create();
let i = 0;
function drawCube() {     
    i++;

    mat4.identity(transformation);
    mat4.rotateZ(transformation, lookTrans, 0.025 * i);
    mat4.translate(transformation, transformation, [0.25, 0, 0]);
    mat4.rotateX(transformation, transformation, 0.5);
    mat4.rotateY(transformation, transformation, 0.5);
    renderer.uniformMatrix4fv('transformation', transformation);

    renderer.clear();
    renderer.bindBuffer(GL.ARRAY_BUFFER, vertBuffer);
    renderer.bufferSubData(GL.ARRAY_BUFFER, 0, geometry.verteices);
    renderer.render(cube);

    requestAnimationFrame(drawCube);                
}

drawCube();

可以點一下完整的範例網頁來看看結果。

若要建立正交投影矩陣,可以使用 mat4ortho,第一個參數是輸出矩陣,也會是函式的傳回值,之後的參數分別為 leftrightbottomtopnearfar,小心別設錯就是了,例如,將〈正交投影矩陣〉中的範例,改寫使用 glMatrix,與矩陣操作相關的部份如下:

renderer.uniformMatrix4fv('projection', 
    mat4.ortho(
        mat4.create(),
        -canvas.clientWidth / 2,  // 左邊界
        canvas.clientWidth / 2,   // 右邊界
        -canvas.clientHeight / 2, // 下邊界
        canvas.clientHeight / 2,  // 上邊界
        0.1,                      // 近面
        canvas.clientWidth        // 遠面
    )
);

let zRotation = mat4.create();
let transformation = mat4.create();

function drawCube() {
    mat4.rotateZ(zRotation, zRotation, 0.025);

    mat4.identity(transformation);
    mat4.translate(transformation, zRotation, [canvas.clientWidth / 8, 0, -canvas.clientWidth / 2]);
    mat4.rotateX(transformation, transformation, 0.5);
    mat4.rotateY(transformation, transformation, 0.5);

    renderer.uniformMatrix4fv('transformation', 
        transformation
    );

    renderer.clear();
    renderer.bindBuffer(GL.ARRAY_BUFFER, vertBuffer);
    renderer.bufferSubData(GL.ARRAY_BUFFER, 0, geometry.verteices);
    renderer.render(cube);

    requestAnimationFrame(drawCube);                
}

drawCube();

可以點一下完整的範例網頁來看看結果。

類似地,若要建立透視投影矩陣,可以使用 perspective,第一個參數是輸出矩陣,也會是函式的傳回值,之後的參數分別為 fovyaspectnearfar,例如,例如,將〈透視投影矩陣〉中的範例,改寫使用 glMatrix,與矩陣操作相關的部份如下:

const perspective = mat4.perspective(
    mat4.create(),
    Math.PI / 4,                               // fovy
    canvas.clientWidth / canvas.clientHeight,  // 寬高比
    0.1,                                       // 近面
    canvas.clientWidth                         // 遠面
);

const ortho = mat4.ortho(
    mat4.create(),
    -canvas.clientWidth / 2,  // 左邊界
    canvas.clientWidth / 2,   // 右邊界
    -canvas.clientHeight / 2, // 下邊界                
    canvas.clientHeight / 2,  // 上邊界
    0.1,                      // 近面
    canvas.clientWidth        // 遠面
);

let projection = perspective;
renderer.uniformMatrix4fv('projection', projection);

let changeProjection = false;
canvas.addEventListener('mousedown', () => {
    changeProjection = true;
});

let transformation = mat4.create();

let i = 0;
function drawCube() {
    if(changeProjection) {
        projection = projection === perspective ? ortho : perspective;
        renderer.uniformMatrix4fv('projection', projection);
        changeProjection = false;
    }
    i++;

    mat4.identity(transformation);

    mat4.translate(transformation, transformation, [0, 0, -canvas.clientWidth / 2]);
    mat4.rotateY(transformation, transformation, 0.025 * i);
    mat4.translate(transformation, transformation, [canvas.clientWidth / 4, 0, 0]);
    mat4.rotateX(transformation, transformation, -0.25);
    mat4.rotateZ(transformation, transformation, 0.25);
    renderer.uniformMatrix4fv('transformation', transformation);

    renderer.clear();
    renderer.bindBuffer(GL.ARRAY_BUFFER, vertBuffer);
    renderer.bufferSubData(GL.ARRAY_BUFFER, 0, geometry.verteices);
    renderer.render(cube);

    requestAnimationFrame(drawCube);                
}

drawCube();

可以點一下完整的範例網頁來看看結果。