9.4 创建一个旋转的立方体
好了,现在真正有趣的事情开始了。本节,我们将创建一个旋转的3D立方体,该立方体具有不同颜色的表面。为了做到这一点,我们将介绍两类新的缓冲区——颜色缓冲区和索引缓冲区。
操作步骤
按照以下步骤,使用WebGL创建一个旋转立方体:
1. 链接到glMatrix库和WebGL包装器:
<script type="text/javascript" src="glMatrix-1.0.1.min.js"> </script>
<script type="text/javascript" src="WebGL.js"> </script>
2. 定义initBuffers()函数,该函数初始化立方体的位置缓冲区,颜色缓冲区,及索引缓冲区:
function initBuffers(gl) {
var cubeBuffers = {}
cubeBuffers.positionBuffer = gl.createArrayBuffer([
// Front face
-1, -1, 1,
1, -1, 1,
1, 1, 1,
-1, 1, 1,
// Back face
-1, -1, -1,
-1, 1, -1,
1, 1, -1,
1, -1, -1,
// Top face
-1, 1, -1,
-1, 1, 1,
1, 1, 1,
1, 1, -1,
// Bottom face
-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,
// Right face
1, -1, -1,
1, 1, -1,
1, 1, 1,
1, -1, 1,
// Left face
-1, -1, -1,
-1, -1, 1,
-1, 1, 1,
-1, 1, -1
]);
//构建彩色顶点
var colors = [
[1, 0, 1, 1], // Front face - Pink
[0, 1, 0, 1], // Back face - Green
[0, 0, 1, 1], // Top face - Blue
[0, 1, 1, 1], // Bottom face - Turquoise
[1, 1, 0, 1], // Right face - Yellow
[1, 0, 0, 1] // Left face - Red
];
var colorVertices = [];
for (var n in colors) {
var color = colors[n];
for (var i=0; i < 4; i++) {
colorVertices = colorVertices.concat(color);
}
}
cubeBuffers.colorBuffer = gl.createArrayBuffer(colorVertices);
cubeBuffers.indexBuffer = gl.createElementArrayBuffer([
0, 1, 2, 0, 2, 3, // Front face
4, 5, 6, 4, 6, 7, // Back face
8, 9, 10, 8, 10, 11, // Top face
12, 13, 14, 12, 14, 15, // Bottom face
16, 17, 18, 16, 18, 19, // Right face
20, 21, 22, 20, 22, 23 // Left face
]);
return cubeBuffers;
}
3. 定义stage()函数,该函数设置透视矩阵,把模型-视图矩阵设置到单位矩阵,平移立方体,旋转立方体,把位置缓冲区、颜色缓冲区、索引缓冲区发送到显卡,最后,因为模型的表面不是三角形,所有要调用drawElements()方法来绘制该立方体:
function stage(gl, cubeBuffers, angle){
// set field of view at 45 degrees
// set viewing range between 0.1 and 100.0 units away.
gl.perspective(45, 0.1, 100);
gl.identity();
// translate model-view matrix
gl.translate(0, 0, -5);
// rotate model-view matrix about x-axis (tilt box downwards)
gl.rotate(Math.PI * 0.15, 1, 0, 0);
// rotate model-view matrix about y-axis
gl.rotate(angle, 0, 1, 0);
gl.pushPositionBuffer(cubeBuffers);
gl.pushColorBuffer(cubeBuffers);
gl.pushIndexBuffer(cubeBuffers);
gl.drawElements(cubeBuffers);
}
4. 页面加载完成后,初始化WebGL包装器对象,把着色器程序设置为“VARYING_COLOR”( 因为每个面是变量,并且依赖于颜色缓冲区、初始化缓冲区),为动画设置stage函数,然后启动动画:
window.onload = function(){
var gl = new WebGL("myCanvas", "experimental-webgl");
gl.setShaderProgram("VARYING_COLOR");
var cubeBuffers = initBuffers(gl);
var angle = 0;
gl.setStage(function(){
// update angle
var angularVelocity = Math.PI / 4; // radians / second
var angleEachFrame = angularVelocity * this.getTimeInterval() / 1000;
angle += angleEachFrame;
this.clear();
stage(this, cubeBuffers, angle);
});
gl.start();
};
5. 在HTML文档的body部分嵌入canvas标签:
<canvas id="myCanvas" width="600" height="250" style="border:1px solid black;">
</canvas>
工作原理
本节介绍了索引缓冲区和颜色缓冲区的概念。在前两节,我们创建了一个三角形的平面,三角形平面的模型是WebGL中最容易实现的,因为它仅仅需要一个缓冲区——位置缓冲区。若要使用非三角形的面来创建一个3D模型,如立方体,就稍微有点复杂,因为我们要把立方体表示为一组三角形的面。我们可以通过创建一个索引缓冲区,把三角形映射为位置缓冲区中的顶点来实现它。
看看前面代码中的索引缓冲区中的顶点。你将会注意到前6个元素是[0, 1, 2, 0, 2, 3]。前三个元素,[0, 1, 2]指的是位置缓冲区中的第0个,第1个,第2个顶点,它们形成一个立方体正面的半个三角形。第二组元素,[0, 2, 3]相当于位置缓冲区中的第0个,第2个,第3个顶点,它们形成一个立方体正面的另外半个三角形。放在一起,这两个三角形形成立方体的正面。完整的索引缓冲区中,将包含构成立方体的6个面在位置缓冲区中顶点的映射。
除了索引缓冲区,本节还需要使用颜色缓冲区。颜色缓冲区用来定义模型各个面的颜色。本节的颜色缓冲区为立方体的6个面,定义了6种不同的颜色。跟索引缓冲区类似,颜色缓冲区用来把颜色映射到位置缓冲区中的每个顶点。某种颜色由[红, 绿, 蓝, 透明度]四个元素定义。根据位置缓冲区的定义,我们的立方体由6个面组成,每个面有4个顶点。因此,我们的元素缓冲区数组应该包含(6 个面) * (4 个顶点每个面) * (4 个元素每种颜色) = 96 个元素。
一旦我们的位置缓冲区、颜色缓冲区、索引缓冲区定义完成,剩下的就只有把每个缓冲区发送到显卡,并渲染模型了。前两节,直接使用drawArrays()方法来渲染三角形,跟前两节不同,本节我们必须使用drawElements()方法,因为我们的模型是由非三角形面组成的,需要一个索引缓冲区来把三角形面映射到模型的各个正方形的面。
相关参考
- 第5章 创建Animation类