版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/78473425
<!--探讨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>