Bootstrap

WebGL进阶(六)投影矩阵

基础理论:

        透视投影下,产生的三维场景看上去更是有深度感,更加自然,因为我们平时观察真实世界用的也是透视投影。在大多数情况下,比如三维射击类游戏中,我们都应当采用透视投影。
        正射投影的好处是用户可以方便地比较场景中物体( 比如两个原子的模型)的大小,这是因为物体看上去的大小与其所在的位置没有关系。在建筑平面图等技术绘图的相关场合,应当使用这种投影。

 

效果:

正射投影

透视投影

MPV变换

源码:

正射投影

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="gl-matrix.js"></script>
    <script>
        let vertexstring = `
        attribute vec4 a_position;
        uniform mat4 u_formMatrix;
        uniform mat4 proj;
        attribute vec4 a_color;
        varying vec4 color;
        void main(void){
            gl_Position =   u_formMatrix * a_position;
            color = a_color;
        }
        `;
        let fragmentstring = `
        precision mediump float;
        varying vec4 color;
        void main(void){
          gl_FragColor =color;
        }
        `;

        var webgl;
        var near=40;
        var far= 0;
       
        function init() {
            initWebgl();
            initShader();
            initBuffer();
            draw();
            inittext(near,far)
            initEvent();
        }
        function inittext(near,far){
            document.getElementById("text").innerHTML = "near:"+near +"<br/>"+ "far:"+far;
        }
        function initEvent() {
            document.onkeydown = handleKeyDown;
        }
        function handleKeyDown(event) {

            if (String.fromCharCode(event.keyCode) == 'W') {
                near += 1;
            }
            else if (String.fromCharCode(event.keyCode) == 'S') {
                near -=1;
            }
            else if (String.fromCharCode(event.keyCode) == 'A') {
                far -= 1;
            }
            else if (String.fromCharCode(event.keyCode) == 'D') {
                far += 1;
            }
            inittext(near,far)
            initBuffer();
            draw();

        }
        function initWebgl() {
            let webglDiv = document.getElementById('myCanvas');
            webgl = webglDiv.getContext("webgl");
            webgl.viewport(0, 0, webglDiv.clientWidth, webglDiv.clientHeight);

        }
        function initShader() {

            let vsshader = webgl.createShader(webgl.VERTEX_SHADER);
            let fsshader = webgl.createShader(webgl.FRAGMENT_SHADER);

            webgl.shaderSource(vsshader, vertexstring);
            webgl.shaderSource(fsshader, fragmentstring);

            webgl.compileShader(vsshader);
            webgl.compileShader(fsshader);
            if (!webgl.getShaderParameter(vsshader, webgl.COMPILE_STATUS)) {
                var err = webgl.getShaderInfoLog(vsshader);
                alert(err);
                return;
            }
            if (!webgl.getShaderParameter(fsshader, webgl.COMPILE_STATUS)) {
                var err = webgl.getShaderInfoLog(fsshader);
                alert(err);
                return;
            }
            let program = webgl.createProgram();
            webgl.attachShader(program, vsshader);
            webgl.attachShader(program, fsshader)
            webgl.linkProgram(program);
            webgl.useProgram(program);
            webgl.program = program

        }
// 初始化一个投影矩阵,并将其设置为单位矩阵
function initBuffer() {
    let ProjMatrix = glMatrix.mat4.create(); // 创建一个新的4x4矩阵
    glMatrix.mat4.identity(ProjMatrix); // 将该矩阵设置为单位矩阵

    // 使用正交投影设置投影矩阵的视野范围
    // 参数分别是:矩阵、左、右、下、上、近、远
    glMatrix.mat4.ortho(ProjMatrix, -50, 50, -100, 100, near, far); // 修改可视域范围

    // 定义顶点数据,包括位置和颜色
    let arr = [
        // 位置坐标,颜色值
        0.0, 70, -40, 1.0,          1, 0,  0, 1, // 红色
        -50, -30, -40, 1.0,         1, 0,  0, 1, // 绿色
        50, -30, -40, 1.0,          1, 0,  0, 1,

        50, 40, -20, 1, 1.0,      1.0,  0.4, 1, // 紫色
        -50, 40, -20, 1, 1.0,     1.0,  0.4, 1, // 紫色
        0.0, -60,-20, 1, 1.0,     1.0,  0.4, 1, // 紫色

        0.0, 50, 0.0, 1,  0.4,    0.4, 1.0, 1, // 青色
        -50, -50, 0.0, 1,  0.4,   0.4, 1.0, 1, // 青色
        50, -50, 0.0, 1,  0.4,    0.4, 1.0, 1, // 青色
    ]

    // 创建一个Float32Array类型的数组来存储顶点数据
    let pointPosition = new Float32Array(arr);

    // 获取顶点着色器中位置属性的位置
    let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");

    // 创建一个缓冲区对象
    let triangleBuffer = webgl.createBuffer();

    // 将缓冲区绑定到ARRAY_BUFFER目标上
    webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);

    // 将顶点数据存储到缓冲区对象中
    webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW);

    // 启用顶点位置属性
    webgl.enableVertexAttribArray(aPsotion);

    // 将缓冲区中的数据解释为顶点位置,并指定步长和偏移量
    webgl.vertexAttribPointer(aPsotion, 4, webgl.FLOAT, false, 8 * 4, 0);

    // 获取顶点着色器中颜色属性的位置
    let aColor = webgl.getAttribLocation(webgl.program, "a_color");

    // 启用顶点颜色属性
    webgl.enableVertexAttribArray(aColor);

    // 将缓冲区中的数据解释为顶点颜色,并指定步长和偏移量
    webgl.vertexAttribPointer(aColor, 4, webgl.FLOAT, false, 8 * 4, 4 * 4);

    // 获取uniform变量u_formMatrix的位置
    let uniformMatrix1 = webgl.getUniformLocation(webgl.program, "u_formMatrix");

    // 将投影矩阵传递给着色器
    webgl.uniformMatrix4fv(uniformMatrix1, false, ProjMatrix);
}
        function draw() {
            webgl.clearColor(0.0, 1.0, 0.0, 1.0);
            webgl.clear(webgl.COLOR_BUFFER_BIT);
            webgl.drawArrays(webgl.TRIANGLES, 0, 9);
        }
      
    </script>
</head>

<body onload="init()">
    <canvas id='myCanvas' width="1024" height='768'></canvas>
    <div id="text"></div>
</body>

</html>

透视投影

  //角度小,看到的物体大,角度大,看到的物体小。
glMatrix.mat4.perspective(ProjMatrix,angle * Math.PI / 180, 
webglDiv.clientWidth/webglDiv.clientHeight,1,100)    //修改可视域范围

MPV变换

// 初始化缓冲区并设置矩阵
function initBuffer() {
    // 定义顶点数据,包括位置和颜色
    let arr = [
        // 位置坐标,颜色值
        0.0, 100, -80, 1,      1, 0,  0, 1, // 红色
        -50, -100, -80, 1,     1, 0,  0, 1,
        50, -100, -80, 1,      1, 0,  0, 1,

        0, 100, -60, 1,       1.0, 1.0,  0.4, 1, // 黄色
        -50, -100, -60, 1,      1.0, 1.0,  0.4, 1,
        50, -100,-60, 1,      1.0, 1.0,  0.4, 1,

        0.0, 100, -35.0, 1,      0.4,  0.4, 1.0, 1, // 蓝色
        -50, -100, -35.0, 1,     0.4,  0.4, 1.0, 1,
        50, -100, -35.0, 1,      0.4, 0.4, 1.0, 1,
    ]

    // 创建一个Float32Array类型的数组来存储顶点数据
    let pointPosition = new Float32Array(arr);

    // 获取顶点着色器中位置属性的位置
    let aPosition = webgl.getAttribLocation(webgl.program, "a_position");

    // 创建一个缓冲区对象
    let triangleBuffer = webgl.createBuffer();
    webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
    webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW);
    webgl.enableVertexAttribArray(aPosition);
    webgl.vertexAttribPointer(aPosition, 4, webgl.FLOAT, false, 8 * 4, 0);

    // 获取顶点着色器中颜色属性的位置
    let aColor = webgl.getAttribLocation(webgl.program, "a_color");
    webgl.enableVertexAttribArray(aColor);
    webgl.vertexAttribPointer(aColor, 4, webgl.FLOAT, false, 8 * 4, 4 * 4);

    // 初始化投影矩阵
    let ProjMatrix = glMatrix.mat4.create();
    glMatrix.mat4.identity(ProjMatrix);
    // 设置透视投影矩阵,参数为:矩阵、角度(转换为弧度)、宽高比、近剪裁面、远剪裁面
    glMatrix.mat4.perspective(ProjMatrix, angle * Math.PI / 180, webglDiv.clientWidth / webglDiv.clientHeight, 1, 1000);

    // 获取uniform变量u_formMatrix的位置
    let uniformMatrix1 = webgl.getUniformLocation(webgl.program, "u_formMatrix");

    // 初始化模型矩阵
    let ModelMatrix = glMatrix.mat4.create();
    glMatrix.mat4.identity(ModelMatrix);
    // 沿x轴平移模型
    glMatrix.mat4.translate(ModelMatrix, ModelMatrix, [180, 0, 0]);

    // 初始化视图矩阵
    let ViewMatrix = glMatrix.mat4.create();
    glMatrix.mat4.identity(ViewMatrix);
    // 设置视图矩阵,参数为:矩阵、相机位置、目标位置、上方向
    glMatrix.mat4.lookAt(ViewMatrix, [0, 0, 300], [0, 0, -90], [0, 1, 0]);

    // 计算模型视图矩阵
    let mvMatrix = glMatrix.mat4.create();
    glMatrix.mat4.multiply(mvMatrix, ViewMatrix, ModelMatrix);

    // 计算模型视图投影矩阵
    let mvpMatrix = glMatrix.mat4.create();
    glMatrix.mat4.identity(mvpMatrix);
    glMatrix.mat4.multiply(mvpMatrix, ProjMatrix, mvMatrix);
    // 将模型视图投影矩阵传递给着色器
    webgl.uniformMatrix4fv(uniformMatrix1, false, mvpMatrix);

    // 设置清除颜色为绿色,并清除颜色缓冲区
    webgl.clearColor(0.0, 1.0, 0.0, 1.0);
    webgl.clear(webgl.COLOR_BUFFER_BIT);

    // 绘制三角形
    webgl.drawArrays(webgl.TRIANGLES, 0, 9);

    // 重置模型矩阵并沿x轴负方向平移
    glMatrix.mat4.identity(ModelMatrix);
    glMatrix.mat4.translate(ModelMatrix, ModelMatrix, [-180, 0, 0]);

    // 重置视图矩阵
    glMatrix.mat4.identity(ViewMatrix);
    glMatrix.mat4.lookAt(ViewMatrix, [0, 0, 300], [0, 0, -90], [0, 1, 0]);

    // 重新计算模型视图矩阵
    glMatrix.mat4.multiply(mvMatrix, ViewMatrix, ModelMatrix);

    // 重新计算模型视图投影矩阵
    glMatrix.mat4.identity(mvpMatrix);
    glMatrix.mat4.multiply(mvpMatrix, ProjMatrix, mvMatrix);

    // 再次将模型视图投影矩阵传递给着色器
    webgl.uniformMatrix4fv(uniformMatrix1, false, mvpMatrix);

    // 再次绘制三角形
    webgl.drawArrays(webgl.TRIANGLES, 0, 9);
}

 

复盘:

;