WEBGL学习【八】模型视图投影矩阵

2023-03-15,,

<!--探讨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>

WEBGL学习【八】模型视图投影矩阵的相关教程结束。

《WEBGL学习【八】模型视图投影矩阵.doc》

下载本文的Word格式文档,以方便收藏与打印。