您的位置:首页 > 其它

38 WebGL针对单独的顶点坐标绘制组成模型

2017-06-11 23:07 316 查看
案例查看地址:点击这里
这一节和上一节绘制的模型是一模一样的效果,就是区别是这一节,对每个模型都定义了单独的一组顶点数据,并存储在了一个单独的缓存区中。通常,一个部件的顶点数据包括坐标、法向量、索引值等,但是这里的每个部件都是立方体,所以你可以让各部件共享法向量和索引值,而仅仅为各个部件单独定义顶点坐标。



<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Title</title>
<style>
body {
margin: 0;
text-align: center;
}

#canvas {
margin: 0;
}
</style>
</head>
<body onload="main()">
<canvas id="canvas" height="800" width="1200"></canvas>
</body>
<script src="lib/webgl-utils.js"></script>
<script src="lib/webgl-debug.js"></script>
<script src="lib/cuon-utils.js"></script>
<script src="lib/cuon-matrix.js"></script>
<script>
// 顶点着色器
var VSHADER_SOURCE = "" +
"attribute vec4 a_Position;\n" + //顶点位置变量
"attribute vec4 a_Normal;\n" + //顶点法向量变量
"uniform mat4 u_MvpMatrix;\n" + //视图模板射影矩阵
"uniform mat4 u_NormalMatrix;\n" + //逆转置矩阵
"varying vec4 v_Color;\n" + //顶点颜色
"void main(){\n" +
" gl_Position = u_MvpMatrix * a_Position;\n" +
" vec3 lightDirection = normalize(vec3(0.0,0.5,0.7));\n" +
" vec4 color = vec4(1.0,0.4,0.0,1.0);\n" +
" vec3 normal = normalize((u_NormalMatrix * a_Normal).xyz);\n" +
" float nDotL = max(dot(normal, lightDirection), 0.0);\n" +
" v_Color = vec4(color.rgb * nDotL + vec3(0.1), color.a);\n" +
"}";

// 片元着色器
var FSHADER_SOURCE = "" +
"#ifdef GL_ES\n" +
"precision mediump float;\n" +
"#endif\n" +
"varying vec4 v_Color;\n" +
"void main () {" +
" gl_FragColor = v_Color;\n" +
"}\n";

//主函数,页面加载完成触发
function main() {
//获取canvas对象
var canvas = document.getElementById("canvas");

//获取WebGL上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console("您的浏览器不支持WebGL");
return;
}

//初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log("无法初始化着色器");
return;
}

//通过创建缓冲区并赋值数据给attribute变量 并返回绘制次数
var n = initVertexBuffers(gl);
if (n < 0) {
console.log("无法设置缓冲区的相关信息");
return;
}

//初始化底色和开启隐藏面消除
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);

//获取相关的attribute变量的存储位置
var a_Position = gl.getAttribLocation(gl.program, "a_Position");

//获取相关的uniform变量的存储位置
var u_MvpMatrix = gl.getUniformLocation(gl.program, "u_MvpMatrix");
var u_NormalMatrix = gl.getUniformLocation(gl.program, "u_NormalMatrix");
if (!u_NormalMatrix || !u_MvpMatrix || a_Position < 0) {
console.log("无法获取到相关的存储位置");
return;
}

//创建一个视点(view) 射影(projection) 矩阵(matrix)
var viewProjMatrix = new Matrix4();
viewProjMatrix.setPerspective(50.0, canvas.width / canvas.height, 1.0, 100.0);
viewProjMatrix.lookAt(20.0, 10.0, 30.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

//添加键盘按键交互事件
document.onkeydown = function (e) {
keydown(e, gl, n, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);
};

//绘制两节胳膊
draw(gl, n, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);
}

//声明全局变量
var angle_step = 3.0; //每一次点击触发事件旋转角度(度)的增量
var g_arm1Angle = -90.0;//arm1的旋转角度(度)
var g_joint1Angle = -45.0;//joint1的旋转角度(度
var g_joint2Angle = 0.0; //joint2的当前角度
var g_joint3Angle = 0.0; //joint3的当前角度

//顶点坐标数据缓冲区变量
var g_baseBuffer, g_arm1Buffer, g_arm2Buffer, g_palmBuffer, g_fingerBuffer;

function keydown(event, gl, n, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix) {
switch (event.keyCode) {
case 38: // 上键 -> 以joint1为中心沿着z轴旋转(增量)
if (g_joint1Angle < 135.0) g_joint1Angle += angle_step;
break;
case 40: // 下键 -> 以joint1为中心沿着z轴旋转(减量)
if (g_joint1Angle > -135.0) g_joint1Angle -= angle_step;
break;
case 39: // 右键 -> 以y轴进行水平旋转(增量)
g_arm1Angle = (g_arm1Angle + angle_step) % 360;
break;
case 37: // 左键 -> 以y轴进行水平旋转(减量)
g_arm1Angle = (g_arm1Angle - angle_step) % 360;
break;
case 90: // Z键 -> 使joint2正向转动
g_joint2Angle = (g_joint2Angle + angle_step) % 360;
break;
case 88: // X键 -> 使joint2负向转动
g_joint2Angle = (g_joint2Angle - angle_step) % 360;
break;
case 86: // V键 -> 使joint3正向转动
if (g_joint3Angle < 60.0) {
g_joint3Angle = (g_joint3Angle + angle_step) % 360;
}
break;
case 67: // C键 -> 使joint3负向转动
if (g_joint3Angle > -60.0) {
g_joint3Angle = (g_joint3Angle - angle_step) % 360;
}
break;
default:
return; // 其他按键没作用
}

draw(gl, n, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);
}

function initVertexBuffers(gl) {
// 每一个模型的顶点坐标数据
var vertices_base = new Float32Array([ // 基座(10x2x10)
5.0, 2.0, 5.0, -5.0, 2.0, 5.0, -5.0, 0.0, 5.0, 5.0, 0.0, 5.0, // v0-v1-v2-v3 front
5.0, 2.0, 5.0, 5.0, 0.0, 5.0, 5.0, 0.0, -5.0, 5.0, 2.0, -5.0, // v0-v3-v4-v5 right
5.0, 2.0, 5.0, 5.0, 2.0, -5.0, -5.0, 2.0, -5.0, -5.0, 2.0, 5.0, // v0-v5-v6-v1 up
-5.0, 2.0, 5.0, -5.0, 2.0, -5.0, -5.0, 0.0, -5.0, -5.0, 0.0, 5.0, // v1-v6-v7-v2 left
-5.0, 0.0, -5.0, 5.0, 0.0, -5.0, 5.0, 0.0, 5.0, -5.0, 0.0, 5.0, // v7-v4-v3-v2 down
5.0, 0.0, -5.0, -5.0, 0.0, -5.0, -5.0, 2.0, -5.0, 5.0, 2.0, -5.0 // v4-v7-v6-v5 back
]);

var vertices_arm1 = new Float32Array([ // 第一节胳膊Arm1(3x10x3)
1.5, 10.0, 1.5, -1.5, 10.0, 1.5, -1.5, 0.0, 1.5, 1.5, 0.0, 1.5, // v0-v1-v2-v3 front
1.5, 10.0, 1.5, 1.5, 0.0, 1.5, 1.5, 0.0, -1.5, 1.5, 10.0, -1.5, // v0-v3-v4-v5 right
1.5, 10.0, 1.5, 1.5, 10.0, -1.5, -1.5, 10.0, -1.5, -1.5, 10.0, 1.5, // v0-v5-v6-v1 up
-1.5, 10.0, 1.5, -1.5, 10.0, -1.5, -1.5, 0.0, -1.5, -1.5, 0.0, 1.5, // v1-v6-v7-v2 left
-1.5, 0.0, -1.5, 1.5, 0.0, -1.5, 1.5, 0.0, 1.5, -1.5, 0.0, 1.5, // v7-v4-v3-v2 down
1.5, 0.0, -1.5, -1.5, 0.0, -1.5, -1.5, 10.0, -1.5, 1.5, 10.0, -1.5 // v4-v7-v6-v5 back
]);

var vertices_arm2 = new Float32Array([ // 第二节胳膊Arm2(4x10x4)
2.0, 10.0, 2.0, -2.0, 10.0, 2.0, -2.0, 0.0, 2.0, 2.0, 0.0, 2.0, // v0-v1-v2-v3 front
2.0, 10.0, 2.0, 2.0, 0.0, 2.0, 2.0, 0.0, -2.0, 2.0, 10.0, -2.0, // v0-v3-v4-v5 right
2.0, 10.0, 2.0, 2.0, 10.0, -2.0, -2.0, 10.0, -2.0, -2.0, 10.0, 2.0, // v0-v5-v6-v1 up
-2.0, 10.0, 2.0, -2.0, 10.0, -2.0, -2.0, 0.0, -2.0, -2.0, 0.0, 2.0, // v1-v6-v7-v2 left
-2.0, 0.0, -2.0, 2.0, 0.0, -2.0, 2.0, 0.0, 2.0, -2.0, 0.0, 2.0, // v7-v4-v3-v2 down
2.0, 0.0, -2.0, -2.0, 0.0, -2.0, -2.0, 10.0, -2.0, 2.0, 10.0, -2.0 // v4-v7-v6-v5 back
]);

var vertices_palm = new Float32Array([ // 手掌Palm(2x2x6)
1.0, 2.0, 3.0, -1.0, 2.0, 3.0, -1.0, 0.0, 3.0, 1.0, 0.0, 3.0, // v0-v1-v2-v3 front
1.0, 2.0, 3.0, 1.0, 0.0, 3.0, 1.0, 0.0, -3.0, 1.0, 2.0, -3.0, // v0-v3-v4-v5 right
1.0, 2.0, 3.0, 1.0, 2.0, -3.0, -1.0, 2.0, -3.0, -1.0, 2.0, 3.0, // v0-v5-v6-v1 up
-1.0, 2.0, 3.0, -1.0, 2.0, -3.0, -1.0, 0.0, -3.0, -1.0, 0.0, 3.0, // v1-v6-v7-v2 left
-1.0, 0.0, -3.0, 1.0, 0.0, -3.0, 1.0, 0.0, 3.0, -1.0, 0.0, 3.0, // v7-v4-v3-v2 down
1.0, 0.0, -3.0, -1.0, 0.0, -3.0, -1.0, 2.0, -3.0, 1.0, 2.0, -3.0 // v4-v7-v6-v5 back
]);

var vertices_finger = new Float32Array([ // 手指Fingers(1x2x1)
0.5, 2.0, 0.5, -0.5, 2.0, 0.5, -0.5, 0.0, 0.5, 0.5, 0.0, 0.5, // v0-v1-v2-v3 front
0.5, 2.0, 0.5, 0.5, 0.0, 0.5, 0.5, 0.0, -0.5, 0.5, 2.0, -0.5, // v0-v3-v4-v5 right
0.5, 2.0, 0.5, 0.5, 2.0, -0.5, -0.5, 2.0, -0.5, -0.5, 2.0, 0.5, // v0-v5-v6-v1 up
-0.5, 2.0, 0.5, -0.5, 2.0, -0.5, -0.5, 0.0, -0.5, -0.5, 0.0, 0.5, // v1-v6-v7-v2 left
-0.5, 0.0, -0.5, 0.5, 0.0, -0.5, 0.5, 0.0, 0.5, -0.5, 0.0, 0.5, // v7-v4-v3-v2 down
0.5, 0.0, -0.5, -0.5, 0.0, -0.5, -0.5, 2.0, -0.5, 0.5, 2.0, -0.5 // v4-v7-v6-v5 back
]);

// 模型的通用法向量
var normals = new Float32Array([
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down
0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0 // v4-v7-v6-v5 back
]);

// 模型的通用的索引数据
var indices = new Uint8Array([
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // right
8, 9, 10, 8, 10, 11, // up
12, 13, 14, 12, 14, 15, // left
16, 17, 18, 16, 18, 19, // down
20, 21, 22, 20, 22, 23 // back
]);

//将坐标值写入缓冲区对象,单不分配给attribute变量
g_baseBuffer = initArrayBufferForlaterUse(gl, vertices_base, 3, gl.FLOAT);
g_arm1Buffer = initArrayBufferForlaterUse(gl, vertices_arm1, 3, gl.FLOAT);
g_arm2Buffer = initArrayBufferForlaterUse(gl, vertices_arm2, 3, gl.FLOAT);
g_palmBuffer = initArrayBufferForlaterUse(gl, vertices_palm, 3, gl.FLOAT);
g_fingerBuffer = initArrayBufferForlaterUse(gl, vertices_finger, 3, gl.FLOAT);
if (!g_baseBuffer || !g_arm1Buffer || !g_arm2Buffer || !g_palmBuffer || !g_fingerBuffer) return -1;

//将法线坐标写入缓冲区,分配给a_Normal并开启之
if (!initArrayBuffer(gl, "a_Normal", normals, gl.FLOAT, 3)) return -1;

//取消缓冲区buffer绑定
gl.bindBuffer(gl.ARRAY_BUFFER, null);

//创建一个缓冲区对象,并将索引绑定到缓冲区
var indexBuffer = gl.createBuffer();
if (!indexBuffer) {
console.log("无法创建索引缓冲区");
return -1;
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

return indices.length;
}

function initArrayBufferForlaterUse(gl, data, num, type) {
var buffer = gl.createBuffer();
if (!buffer) {
console.log("无法创建缓冲区");
return false;
}

//将数据写入缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

//保存一些数据在将来供attribute变量使用
buffer.num = num;
buffer.type = type;

return buffer;
}

function initArrayBuffer(gl, attribute, data, type, num) {
//创建缓冲区
var buffer = gl.createBuffer();
if (!buffer) {
console.log("无法创建缓冲区");
return false;
}

//将数据写入缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

//获取到attribute变量的存储位置,并将变量绑定缓冲区
var a_attribute = gl.getAttribLocation(gl.program, attribute);
if (a_attribute < 0) {
console.log("无法获取到变量的" + attribute + "存储位置");
return false;
}
gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);

//开启缓冲区
gl.enableVertexAttribArray(a_attribute);

return true;
}

//声明两个全局的变换矩阵(模型变换矩阵和模型视图射影矩阵)
var g_modelMatrix = new Matrix4(), g_mvpMatrix = new Matrix4();

//绘制图形
function draw(gl, n, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix) {
//绘制底色
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

//绘制基座
var baseHeight = 2.0;//基座的高度
g_modelMatrix.setTranslate(0.0, -12.0, 0.0);//设置为平移矩阵,并沿y轴向下移动12
drawSegment(gl, n, g_baseBuffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);

//arm1 第一节胳膊
var arm1Length = 10.0;//第一节胳膊的长度
g_modelMatrix.translate(0.0, baseHeight, 0.0);//移至基座
g_modelMatrix.rotate(g_arm1Angle, 0.0, 1.0, 0.0);//沿y轴旋转
drawSegment(gl, n, g_arm1Buffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);

//arm2 第二节胳膊
var arm2Length = 10.0;//
g_modelMatrix.translate(0.0, arm1Length, 0.0);//移至joint1
g_modelMatrix.rotate(g_joint1Angle, 0.0, 0.0, 1.0);//沿z轴旋转
drawSegment(gl, n, g_arm2Buffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);

//机器人手掌的制作
var palmLength = 2.0;
g_modelMatrix.translate(0.0, arm2Length, 0.0);//移至joint2
g_modelMatrix.rotate(g_joint2Angle, 0.0, 1.0, 0.0);//沿y轴旋转
drawSegment(gl, n, g_palmBuffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);

//移至手掌一端的中点
g_modelMatrix.translate(0.0, palmLength, 0.0);

//绘制finger1 第一个手指头
pushMatrix(g_modelMatrix);//将当前的g_modelMatrix的值保存
g_modelMatrix.translate(0.0, 0.0, 2.0);//沿z轴移动
g_modelMatrix.rotate(g_joint3Angle, 1.0, 0.0, 0.0);//沿x轴旋转
drawSegment(gl, n, g_fingerBuffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);
g_modelMatrix = popMatrix();//获取到压入时的g_modelMatrix矩阵的值

//绘制finger2 第二个手指头
g_modelMatrix.translate(0.0, 0.0, -2.0);//沿z轴负方向移动
g_modelMatrix.rotate(-g_joint3Angle, 1.0, 0.0, 0.0);//沿x轴旋转
drawSegment(gl, n, g_fingerBuffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);

}

var g_matrixStack = []; //存储矩阵的值
function pushMatrix(m) {//将矩阵压入栈
var m2 = new Matrix4(m);
g_matrixStack.push(m2);
}

function popMatrix() {//从栈中弹出矩阵
return g_matrixStack.pop();
}

var g_normalMatrix = new Matrix4(); //法线坐标变换矩阵

//每个模型单独绘制
function drawSegment(gl, n, buffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix) {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
//将缓冲区对象分配给attribute变量
gl.vertexAttribPointer(a_Position, buffer.num, buffer.type, false, 0, 0);
//开启变量
gl.enableVertexAttribArray(a_Position);

//计算出计算模型视图矩阵,并赋值给u_MvpMatrix变量
g_mvpMatrix.set(viewProjMatrix);
g_mvpMatrix.multiply(g_modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, g_mvpMatrix.elements);

//获取模型矩阵的逆转置矩阵,并赋值u_NormalMatrix
g_normalMatrix.setInverseOf(g_modelMatrix);
g_normalMatrix.transpose();
gl.uniformMatrix4fv(u_NormalMatrix, false, g_normalMatrix.elements);

//绘制
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

}

</script>
</html>示例程序的关键点:
(1)为每个部件单独创建一个缓冲区,在其中存储顶点的坐标数据。
(2)绘制部件之前,将相应缓冲区对象分配给a_Position变量
(3)开启a_Position变量并绘制改部件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: