当前位置: 首页 > 文档资料 > HTML5 Canvas 实战 >

9.4 创建一个旋转的立方体

优质
小牛编辑
126浏览
2023-12-01

好了,现在真正有趣的事情开始了。本节,我们将创建一个旋转的3D立方体,该立方体具有不同颜色的表面。为了做到这一点,我们将介绍两类新的缓冲区——颜色缓冲区和索引缓冲区。

旋转的立方体
图9-3 旋转的立方体

操作步骤

按照以下步骤,使用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类