zoukankan      html  css  js  c++  java
  • WEBGL学习【八】模型视图投影矩阵

    <!--探讨WEBGL中不同图形的绘制方法:[待测试2017.11.6]-->
    <!DOCTYPE HTML>
    <html lang="en">
    <head>
        <title>WEBGL高级编程----绘制三维场景(变换矩阵)</title>
        <meta charset="utf-8">
        <!--顶点着色器-->
        <script id="shader-vs" type="x-shader/x-vertex">
          attribute vec3 aVertexPosition;
          attribute vec4 aVertexColor;
    
          uniform mat4 uMVMatrix;
          uniform mat4 uPMatrix;
    
          varying vec4 vColor;
    
          void main() {
            vColor = aVertexColor;
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
          }
    
        </script>
    
        <!--片元着色器-->
        <script id="shader-fs" type="x-shader/x-fragment">
          precision mediump float;
    
          varying vec4 vColor;
          void main() {
            gl_FragColor = vColor;
          }
    
        </script>
    
        <!--引入我的库文件-->
        <script src="./lib/webgl-debug.js"></script>
    
        <script type="text/javascript">
            var gl;
            var canvas;
            var shaderProgram;
    
            //绘制桌子的场景模型
            //地板的顶点位置和索引
            var floorVertexPositionBuffer;
            var floorVertexIndexBuffer;
    
            //立方体的顶点位置和索引
            var cubeVertexPositionBuffer;
            var cubeVertexIndexBuffer;
    
            //我的模型 视图 投影矩阵
            var modelViewMatrix;
            var projectionMatrix;
    
            //模型视图的矩阵栈
            var modelViewMatrixStack;
    
    
    
            //创建我的上下文句柄
            function createGLContext(canvas) {
                var names = ["webgl", "experimental-webgl"];
                var context = null;
                for (var i = 0; i < names.length; i++) {
                    try {
                        context = canvas.getContext(names[i]);
                    } catch (e) {
                    }
                    if (context) {
                        break;
                    }
                }
                if (context) {
                    context.viewportWidth = canvas.width;
                    context.viewportHeight = canvas.height;
                } else {
                    alert("Failed to create WebGL context!");
                }
                return context;
            }
    
            //JavaScript代码中通过DOM加载着色器
            function loadShaderFromDOM(id) {
                var shaderScript = document.getElementById(id);
    
                // If we don't find an element with the specified id
                // we do an early exit
                if (!shaderScript) {
                    return null;
                }
    
                // Loop through the children for the found DOM element and
                // build up the shader source code as a string
                var shaderSource = "";
                var currentChild = shaderScript.firstChild;
                while (currentChild) {
                    if (currentChild.nodeType == 3) { // 3 corresponds to TEXT_NODE
                        shaderSource += currentChild.textContent;
                    }
                    currentChild = currentChild.nextSibling;
                }
    
                var shader;
                if (shaderScript.type == "x-shader/x-fragment") {
                    shader = gl.createShader(gl.FRAGMENT_SHADER);
                } else if (shaderScript.type == "x-shader/x-vertex") {
                    shader = gl.createShader(gl.VERTEX_SHADER);
                } else {
                    return null;
                }
    
                gl.shaderSource(shader, shaderSource);
                gl.compileShader(shader);
    
                if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                    alert(gl.getShaderInfoLog(shader));
                    return null;
                }
                return shader;
            }
    
            //设置我的着色器(完成着色器的部分初始化操作)
            function setupShaders() {
                var vertexShader = loadShaderFromDOM("shader-vs");
                var fragmentShader = loadShaderFromDOM("shader-fs");
    
                shaderProgram = gl.createProgram();
                gl.attachShader(shaderProgram, vertexShader);
                gl.attachShader(shaderProgram, fragmentShader);
                gl.linkProgram(shaderProgram);
    
                if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                    alert("Failed to setup shaders");
                }
    
                gl.useProgram(shaderProgram);
    
                //获取顶点的位置和颜色的存储地址
                shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
                shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
    
                //回去模型视图矩阵的存储地址
                shaderProgram.uniformMVMatrix = gl.getUniformLocation(shaderProgram, "uMVMatrix");
                shaderProgram.uniformProjMatrix = gl.getUniformLocation(shaderProgram, "uPMatrix");
    
                gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
    
                //开始创建我的模型视图矩阵
                modelViewMatrix = mat4.create();
                projectionMatrix = mat4.create();
                modelViewMatrixStack = [];
            }
    
            //定义两个辅助函数(矩阵的入栈和出栈操作)
            function pushModelViewMatrix() {
                //把当前的模型视图矩阵存储在一个JavaScript数组中去
                var copyToPush = mat4.create(modelViewMatrix);
                modelViewMatrixStack.push(copyToPush);
            }
    
            function popModelViewMatrix() {
                if (modelViewMatrixStack.length == 0) {
                    throw "Error popModelViewMatrix() - Stack was empty";
                }
                //把我自己保存的模型视图矩阵弹出去
                modelViewMatrix = modelViewMatrixStack.pop();
            }
    
            //处理物体运动的一些基本参数
            var xRot = 0;
            var xSpeed = 3;
    
            var yRot = 0;
            var ySpeed = -3;
    
            var z = -5.0;
    
    
            //可以处理多个按键同时按下的情形
            var currentlyPressedKeys = {};
    
            function handleKeyDown(event) {
                currentlyPressedKeys[event.keyCode] = true;
            }
    
            function handleKeyUp(event) {
                currentlyPressedKeys[event.keyCode] = false;
            }
    
    
            //根据不同的按键做出不同的反应
            function handleKeys() {
                if (currentlyPressedKeys[33]) {
                    // Page Up
                    z -= 0.05;
                }
                if (currentlyPressedKeys[34]) {
                    // Page Down
                    z += 0.05;
                }
                if (currentlyPressedKeys[37]) {
                    // Left cursor key
                    ySpeed -= 1;
                }
                if (currentlyPressedKeys[39]) {
                    // Right cursor key
                    ySpeed += 1;
                }
                if (currentlyPressedKeys[38]) {
                    // Up cursor key
                    xSpeed -= 1;
                }
                if (currentlyPressedKeys[40]) {
                    // Down cursor key
                    xSpeed += 1;
                }
            }
    
            //地板的顶点位置参数
            function setupFloorBuffers() {
                /****第一步****/
                //1.创建地板顶点缓冲区
                floorVertexPositionBuffer = gl.createBuffer();
                //2.绑定缓冲区到目标对象
                gl.bindBuffer(gl.ARRAY_BUFFER, floorVertexPositionBuffer);
                var floorVertexPosition = [
                    // Plane in y=0
                    5.0,   0.0,  5.0,  //v0
                    5.0,   0.0, -5.0,  //v1
                    -5.0,   0.0, -5.0,  //v2
                    -5.0,   0.0,  5.0   //v3
                ];
                //3.向缓冲区对象中写入数据
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(floorVertexPosition), gl.STATIC_DRAW);
    
                floorVertexPositionBuffer.itemSize = 3;
                floorVertexPositionBuffer.numberOfItems = 4;
    
                /****第二步***/
                //1.创建地板顶点索引缓冲区.
                floorVertexIndexBuffer = gl.createBuffer();
                //2.绑定缓冲区到目标对象
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, floorVertexIndexBuffer);
                var floorVertexIndices = [
                    0, 1, 2, 3
                ];
                //3.向缓冲区对象中写入数据(16为无符号整型数字)
                gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(floorVertexIndices), gl.STATIC_DRAW);
    
                floorVertexIndexBuffer.itemSize = 1;
                floorVertexIndexBuffer.numberOfItems = 4;
    
            }
    
            //立方体的顶点位置参数
            function setupCubeBuffers() {
                /*****立方体的顶点位置*****/
                //1.创建立方体的顶点缓冲区
                cubeVertexPositionBuffer = gl.createBuffer();
                //2.绑定缓冲区到目标对象
                gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
                var cubeVertexPosition = [
                    // Front face
                    1.0,  1.0,  1.0, //v0
                    -1.0,  1.0,  1.0, //v1
                    -1.0, -1.0,  1.0, //v2
                    1.0, -1.0,  1.0, //v3
    
                    // Back face
                    1.0,  1.0, -1.0, //v4
                    -1.0,  1.0, -1.0, //v5
                    -1.0, -1.0, -1.0, //v6
                    1.0, -1.0, -1.0, //v7
    
                    // Left face
                    -1.0,  1.0,  1.0, //v8
                    -1.0,  1.0, -1.0, //v9
                    -1.0, -1.0, -1.0, //v10
                    -1.0, -1.0,  1.0, //v11
    
                    // Right face
                    1.0,  1.0,  1.0, //12
                    1.0, -1.0,  1.0, //13
                    1.0, -1.0, -1.0, //14
                    1.0,  1.0, -1.0, //15
    
                    // Top face
                    1.0,  1.0,  1.0, //v16
                    1.0,  1.0, -1.0, //v17
                    -1.0,  1.0, -1.0, //v18
                    -1.0,  1.0,  1.0, //v19
    
                    // Bottom face
                    1.0, -1.0,  1.0, //v20
                    1.0, -1.0, -1.0, //v21
                    -1.0, -1.0, -1.0, //v22
                    -1.0, -1.0,  1.0, //v23
                ];
                //3.向缓冲区对象中写入数据
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeVertexPosition), gl.STATIC_DRAW);
    
                //错误提示:Uncaught TypeError: Cannot read property 'toString' of undefined            /*cubeVertexPosition.itemSize = 3;
                 cubeVertexPosition.numberOfItems = 24;*/
                /*
                * 【经验话语】:如果控制台提示toString()类型的错误,多半原因是由于调用该函数的语句中的某一个变量没有正确定义,或者没有正确初始化操作
                * 【解决方案】:通常检查出错语句中的变量,是否正确赋值!
                * */
                cubeVertexPositionBuffer.itemSize = 3;
                cubeVertexPositionBuffer.numberOfItems = 24;
    
    
                /*****立方体的顶点位置索引信息****/
                //1.创建立方体顶点位置索引缓冲区
                cubeVertexIndexBuffer = gl.createBuffer();
                //2.绑定缓冲区到目标对象
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
                var cubeVertexIndices = [
                    0, 1, 2,      0, 2, 3,    // Front face
                    4, 6, 5,      4, 7, 6,    // Back face
                    8, 9, 10,     8, 10, 11,  // Left face
                    12, 13, 14,   12, 14, 15, // Right face
                    16, 17, 18,   16, 18, 19, // Top face
                    20, 22, 21,   20, 23, 22  // Bottom face
                ];
                //3.向缓冲区对象中写入数据
                gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
    
                //索引之间互相独立, 共计有36个索引点的信息
                cubeVertexIndexBuffer.itemSize = 1;
                cubeVertexIndexBuffer.numberOfItems = 36;
            }
    
    
            //设置我的缓冲区
            function setupBuffers() {
                //设置缓冲区分解为两个部分
                //1.设置地板的顶点缓冲区
                setupFloorBuffers();
                //2.设置立方体的顶点缓冲区
                setupCubeBuffers();
            }
    
            //把我的模型视图矩阵传给顶点着色器
            function uploadModelViewMatrixToShader() {
                gl.uniformMatrix4fv(shaderProgram.uniformMVMatrix, false, modelViewMatrix);
            }
            //把我的投影矩阵传给顶点着色器
            function uploadProjectionMatrixToShader() {
                gl.uniformMatrix4fv(shaderProgram.uniformProjMatrix, false, projectionMatrix);
            }
    
    
            //绘制地板
            function drawFloor(r, g, b, a) {
                //指定一个常量颜色
                gl.disableVertexAttribArray(shaderProgram.vertexColorAttribute);
                gl.vertexAttrib4f(shaderProgram.vertexColorAttribute, r, g, b, a);
    
                //开始绘制(先把地板顶点位置信息传给顶点着色器)
                gl.bindBuffer(gl.ARRAY_BUFFER, floorVertexPositionBuffer);
                gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,
                    floorVertexPositionBuffer.itemSize,
                    gl.FLOAT, false, 0, 0);
                //利用顶点索引信息开始绘图
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, floorVertexIndexBuffer);
                gl.drawElements(gl.TRIANGLE_FAN, floorVertexIndexBuffer.numberOfItems, gl.UNSIGNED_SHORT, 0);
    
            }
    
            //绘制立方体
            function drawCube(r, g, b, a) {
                //设置指定的颜色
                gl.disableVertexAttribArray(shaderProgram.vertexColorAttribute);
                gl.vertexAttrib4f(shaderProgram.vertexColorAttribute, r, g, b, a);
    
                //开始绘制
                gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
                gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    
                //开始利用索引坐标绘制立方体
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
                gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numberOfItems,
                    gl.UNSIGNED_SHORT, 0);
    
            }
    
    
            //绘制卓子
            function drawTable() {
                //绘制之前先保存当前的模型视图矩阵
                pushModelViewMatrix();//最初的模型视图矩阵
                //alert(modelViewMatrixStack.length); 此时这里面存放了两个模型视图矩阵
                //向上移动1个单位
                mat4.translate(modelViewMatrix, [0.0, 1.0, 0.0], modelViewMatrix);
                //开始缩放
                mat4.scale(modelViewMatrix, [2.0, 0.1, 2.0], modelViewMatrix);
                //把平移并且缩放后的矩阵(此时的模型视图矩阵)传到顶点着色器
                uploadModelViewMatrixToShader();
    
                //开始绘制立方体(主要是把立方体的顶点位置传给顶点着色器,然后顶点着色器就会对这个顶点位置向量进行矩阵变换)
                drawCube(0.72, 0.53, 0.04, 1.0);
                popModelViewMatrix();
    
                //绘制桌子腿
                for (var i=-1; i<=1; i+=2) {
                    for (var j= -1; j<=1; j+=2) {
                        pushModelViewMatrix();
                        mat4.translate(modelViewMatrix, [i*1.9, -0.1, j*1.9], modelViewMatrix);
                        mat4.scale(modelViewMatrix, [0.1, 1.0, 0.1], modelViewMatrix);
                        uploadModelViewMatrixToShader();
                        //绘制立方体(会把立方体的顶点坐标,进行矩阵变换)
                        drawCube(0.72, 0.53, 0.04, 1.0); // argument sets brown color
                        popModelViewMatrix();
                    }
                }
            }
    
            //绘图函数
            function draw() {
                //设置视口,清空深度缓存(左下角坐标, 宽度, 高度)
                gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
                gl.clear(gl.COLOR_BUFFER_BIT);
    
                //设置我的透视投影矩阵
                mat4.perspective(60, gl.viewportWidth/gl.viewportHeight, 0.1, 100, projectionMatrix);
                //重置模型视图矩阵
                mat4.identity(modelViewMatrix);
                mat4.lookAt([8, 5, -10], [0, 0, 0], [0, 1, 0], modelViewMatrix);
    
                //在这里设置转动整个场景
                mat4.translate(modelViewMatrix, [0, 0, z], modelViewMatrix);
                mat4.rotateY(modelViewMatrix, yRot, modelViewMatrix);
    
                //把当前的模型视图投影矩阵传递给顶点着色器(到这里顶点的坐标已经转换为了裁剪坐标系)
                uploadModelViewMatrixToShader();
                uploadProjectionMatrixToShader();
    
                //开始绘制地板
                drawFloor(1.0, 0.0, 0.0, 1.0);
    
                //绘制桌子
                //绘制之前先把我当前的WEBGL坐标系统的矩阵保存起来【相当于是桌子腿的最下面位置】
                pushModelViewMatrix();
                //此时物体的坐标系到达桌子的最上表面(注意物体自身的坐标系和WEBGL坐标系的区别)
                //这里实际上是把WEBGL坐标系移动到物体坐标系的原点位置
                mat4.translate(modelViewMatrix, [0.0, 1.1, 0.0], modelViewMatrix);
                uploadModelViewMatrixToShader();
                drawTable();
                popModelViewMatrix();
    
                //开始绘制桌子上面的一个物体
                pushModelViewMatrix();
                mat4.translate(modelViewMatrix, [0.0, 2.7, 0.0], modelViewMatrix);
                //把原来的立方体2*2*2变换为1*1*1的立方体,相当于把长宽高都缩减为原来的一半
                mat4.scale(modelViewMatrix, [0.5, 0.5, 0.5], modelViewMatrix);
                uploadModelViewMatrixToShader();
                drawCube(0.0, 0.0, 1.0, 1.0);
                popModelViewMatrix();
    
                //在绘制一个图形
                pushModelViewMatrix();
                mat4.translate(modelViewMatrix, [0.0, 3.7, 0.0], modelViewMatrix);
                mat4.scale(modelViewMatrix, [0.5, 0.5, 0.5], modelViewMatrix);
                uploadModelViewMatrixToShader();
                drawCube(0.0, 1.0, 0.0, 1.0);
                popModelViewMatrix();
    
                pushModelViewMatrix();
                mat4.translate(modelViewMatrix, [0.0, 4.7, 0.0], modelViewMatrix);
                mat4.scale(modelViewMatrix, [0.5, 0.5, 0.5], modelViewMatrix);
                //mat4.rotateY(modelViewMatrix, yRot, modelViewMatrix);
                uploadModelViewMatrixToShader();
                drawCube(1.0, 1.0, 0.0, 1.0);
                popModelViewMatrix();
    
                //开始转动场景
                /*mat4.rotate(modelViewMatrix, xRot, [1, 0, 0], modelViewMatrix);
                mat4.rotateY(modelViewMatrix, yRot, modelViewMatrix);
                uploadModelViewMatrixToShader();
                uploadProjectionMatrixToShader();*/
    
    
            }
    
            var lastTime = 0;
            //实时更新旋转角度
            function animate() {
                var timeNow = new Date().getTime();
                if (lastTime != 0) {
                    var elapsed = timeNow - lastTime;
    
                    xRot += (xSpeed * elapsed) / 1000.0;
                    yRot += (ySpeed * elapsed) / 1000.0;
                }
                lastTime = timeNow;
            }
    
    
            //不断重绘场景
            function tick() {
                requestAnimationFrame(tick);
                handleKeys();
                draw();
                animate();
            }
    
            function startup() {
                canvas = document.getElementById("myGLCanvas");
                gl = WebGLDebugUtils.makeDebugContext(createGLContext(canvas));
                setupShaders();
                setupBuffers();
                gl.clearColor(1.0, 1.0, 1.0, 1.0);
    
    
                //逆时针方向是前面
                gl.frontFace(gl.CW);
                //激活背面剔除功能
                gl.enable(gl.CULL_FACE);
                //WEBGL剔除背面三角形
                gl.cullFace(gl.FRONT);
    
                //draw();
    
                document.onkeydown = handleKeyDown;
                document.onkeyup = handleKeyUp;
    
                tick();
            }
        </script>
        <script src="./lib/glMatrix.js"></script>
    
    </head>
    
    <body onload="startup();">
    <canvas id="myGLCanvas" width="500" height="500" style="border: 2px solid springgreen;"></canvas>
    </body>
    
    </html>
    
  • 相关阅读:
    JavaScript学习总结(5)——Javascript面向(基于)对象编程
    JavaScript学习总结(4)——JavaScript数组
    高性能Web动画和渲染原理系列(4)“Compositor-Pipeline演讲PPT”学习摘要【华为云技术分享】
    AI:为你写诗,为你做不可能的事
    鲲鹏性能优化十板斧(二)——CPU与内存子系统性能调优
    鲲鹏性能优化十板斧——鲲鹏处理器NUMA简介与性能调优五步法
    华为鲲鹏云之我见
    一站式应用平台,华为云实现自动化构建知识图谱
    化鲲为鹏,我有话说 ,鲲鹏ARM架构的优势
    【读一本书】《昇腾AI处理器架构与编程》--神经网络基本知识学习(1)
  • 原文地址:https://www.cnblogs.com/52tech/p/9325116.html
Copyright © 2011-2022 走看看