WebGL之旅(十一)透视投影

来源:互联网 发布:codol 正义 数据 编辑:程序博客网 时间:2024/06/06 00:24

与正射投影不同,透视投影会出近大远小的效果,与人的视觉效果一直,游戏中一般都是使用的透视投影。

示例:

/** * 透视投影矩阵 * xu.lidong@qq.com * */var g_vs = `attribute vec4 a_Position;attribute vec4 a_Color;uniform mat4 u_ViewMat;uniform mat4 u_ProjMat;varying vec4 v_Color;void main() {    gl_Position = u_ProjMat * u_ViewMat * a_Position;    v_Color = a_Color;}`;var g_fs = `precision mediump float;varying vec4 v_Color;void main(){    gl_FragColor = v_Color;}`;function main() {    var gl = getGL();    var shaderProgram = initShader(gl);    var n = initVertexBuffers(gl, shaderProgram);    draw(gl, shaderProgram, n);}function getGL() {    var canvas = document.getElementById("container");    return canvas.getContext("webgl") || canvas.getContext("experimental-webgl");}function initShader(gl) {    var vs = gl.createShader(gl.VERTEX_SHADER);    gl.shaderSource( vs, g_vs);    gl.compileShader(vs);    var fs = gl.createShader(gl.FRAGMENT_SHADER);    gl.shaderSource( fs, g_fs);    gl.compileShader(fs);    var shaderProgram = gl.createProgram();    gl.attachShader(shaderProgram, vs);    gl.attachShader(shaderProgram, fs);    gl.linkProgram(shaderProgram);    gl.useProgram(shaderProgram);    return shaderProgram;}function initVertexBuffers(gl, shaderProgram) {    var verticesColors = new Float32Array([        0.75,  1.0,  -4.0,  0.4,  1.0,  0.4,        0.25, -1.0,  -4.0,  0.4,  1.0,  0.4,        1.25, -1.0,  -4.0,  1.0,  0.4,  0.4,        0.75,  1.0,  -2.0,  1.0,  1.0,  0.4,        0.25, -1.0,  -2.0,  1.0,  1.0,  0.4,        1.25, -1.0,  -2.0,  1.0,  0.4,  0.4,        0.75,  1.0,   0.0,  0.4,  0.4,  1.0,        0.25, -1.0,   0.0,  0.4,  0.4,  1.0,        1.25, -1.0,   0.0,  1.0,  0.4,  0.4,        -0.75,  1.0,  -4.0,  0.4,  1.0,  0.4,        -1.25, -1.0,  -4.0,  0.4,  1.0,  0.4,        -0.25, -1.0,  -4.0,  1.0,  0.4,  0.4,        -0.75,  1.0,  -2.0,  1.0,  1.0,  0.4,        -1.25, -1.0,  -2.0,  1.0,  1.0,  0.4,        -0.25, -1.0,  -2.0,  1.0,  0.4,  0.4,        -0.75,  1.0,   0.0,  0.4,  0.4,  1.0,        -1.25, -1.0,   0.0,  0.4,  0.4,  1.0,        -0.25, -1.0,   0.0,  1.0,  0.4,  0.4,    ]);    var FSIZE = verticesColors.BYTES_PER_ELEMENT;    var vertexColorBuffer = gl.createBuffer();    gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);    gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);    var a_Position = gl.getAttribLocation(shaderProgram, "a_Position");    gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);    gl.enableVertexAttribArray(a_Position);    var a_Color = gl.getAttribLocation(shaderProgram, "a_Color");    gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);    gl.enableVertexAttribArray(a_Color);    return verticesColors.length / 6;}function draw(gl, shaderProgram, n) {    var u_ProjMat = gl.getUniformLocation(shaderProgram, "u_ProjMat");    var projMat = getPerspectiveProjection(60, 1280/720, 1, 100);    gl.uniformMatrix4fv(u_ProjMat, false, projMat);    var u_ViewMat = gl.getUniformLocation(shaderProgram, "u_ViewMat");    var viewMat = lookAt(0, 0, 6, 0, 0, -100, 0, 1, 0);    gl.uniformMatrix4fv(u_ViewMat, false, viewMat);    gl.clearColor(0.0, 0.0, 0.0, 1.0);    gl.clear(gl.COLOR_BUFFER_BIT);    gl.drawArrays(gl.TRIANGLES, 0, n);}function getPerspectiveProjection(fov, aspect, near, far) {    var fovy = Math.PI * fov / 180 / 2;    var s = Math.sin(fovy);    var rd = 1 / (far - near);    var ct = Math.cos(fovy) / s;    return new Float32Array([        ct / aspect, 0, 0, 0,        0, ct, 0, 0,        0, 0, -(far + near) * rd, -1,        0, 0, -2 * near * far * rd, 0,    ]);}/** *  以下代码为lookAt的实现 * *//** * 由平移向量获取平移矩阵 * */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 lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {    var zAxis = subVector([centerX, centerY, centerZ], [eyeX, eyeY, eyeZ]);    var N = normalizeVector(zAxis);    var xAxis = crossMultiVector(N, [upX, upY, upZ]);    var U = normalizeVector(xAxis);    var V = crossMultiVector(U, N);    // 旋转的逆矩阵    var r = new Float32Array([        U[0], V[0], -N[0], 0,        U[1], V[1], -N[1], 0,        U[2], V[2], -N[2], 0,        0, 0, 0, 1    ]);    // 平移的逆矩阵    var t = getTranslationMatrix(-eyeX, -eyeY, -eyeZ);    return multiMatrix44(r, t);}/** * 由缩放因子获取缩放矩阵 * */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,    ]);}/** * 向量点乘 * */function dotMultiVector(v1, v2) {    var res = 0;    for (var i = 0; i < v1.length; i++) {        res += v1[i] * v2[i];    }    return res;}/** * 向量叉乘 * */function crossMultiVector(v1, v2) {    return [        v1[1] * v2[2] - v1[2] * v2[1],        v1[2] * v2[0] - v1[0] * v2[2],        v1[0] * v2[1] - v1[1] * v2[0]    ];}/** * 向量减法 * */function subVector(v1, v2){    return [v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]];}/** * 向量加法 * */function addVector(v1, v2){    return [v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]];}/** * 向量归一化 * */function normalizeVector(v) {    var len = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);    return (len > 0.00001) ? [v[0]/len, v[1]/len, v[2]/len] : [0, 0, 0];}/** * 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);}

上列中,前后绘制了分三排绘制了9个三角形,可以看到,距离视点越远的三角形越小,如图:

这里写图片描述

从图中可以看到,遮挡关系是不正确的,前面的物品被后面的遮挡住了,修改draw函数如下:

function draw(gl, shaderProgram, n) {    var u_ProjMat = gl.getUniformLocation(shaderProgram, "u_ProjMat");    var projMat = getPerspectiveProjection(60, 1280/720, 1, 100);    gl.uniformMatrix4fv(u_ProjMat, false, projMat);    var u_ViewMat = gl.getUniformLocation(shaderProgram, "u_ViewMat");    var viewMat = lookAt(0, 0, 6, 0, 0, -100, 0, 1, 0);    gl.uniformMatrix4fv(u_ViewMat, false, viewMat);    gl.clearColor(0.0, 0.0, 0.0, 1.0);    gl.enable(gl.DEPTH_TEST);// 开启深度测试    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);// 增加清空深度缓冲区    gl.drawArrays(gl.TRIANGLES, 0, n);}

然后就正确了,如图:

这里写图片描述