绘制正方体

下面是代码演示

经过前面7篇文章的铺垫,绘制正方体已经不是什么难事了。正方体其实就是由6个矩形组成的几何体,X,Y,Z轴上各两个矩形。下面是Z轴上两个矩形的示意图。

绘制Z轴需要的顶点数据如下。

-0.5,   0.5,    0.5,  
-0.5,   -0.5,   0.5, 
0.5,    -0.5,   0.5, 
0.5,    -0.5,   0.5,  
0.5,    0.5,    0.5,   
-0.5,   0.5,    0.5, 

-0.5,   0.5,    -0.5, 
-0.5,   -0.5,   -0.5,
0.5,    -0.5,   -0.5,
0.5,    -0.5,   -0.5, 
0.5,    0.5,    -0.5,  
-0.5,   0.5,    -0.5,

其他轴的平面绘制规则一样,将Z轴数据第三列交换到第一列或者第二列即可生成X,Y轴上的矩形数据。

// X轴上的平面
0.5,    -0.5,   0.5,
0.5,    -0.5,   -0.5,
0.5,    0.5,    -0.5,
0.5,    0.5,    -0.5,
0.5,    0.5,    0.5,
0.5,    -0.5,   0.5,
-0.5,   -0.5,   0.5,
-0.5,   -0.5,   -0.5,
-0.5,   0.5,    -0.5,
-0.5,   0.5,    -0.5,
-0.5,   0.5,    0.5,
-0.5,   -0.5,   0.5,
// Y轴上的平面
-0.5,   0.5,    0.5,  
-0.5,   0.5,    -0.5,  
0.5,    0.5,    -0.5,  
0.5,    0.5,    -0.5,  
0.5,    0.5,    0.5,  
-0.5,   0.5,    0.5,  
-0.5,   -0.5,   0.5,
-0.5,   -0.5,   -0.5,
0.5,    -0.5,   -0.5,
0.5,    -0.5,   -0.5,
0.5,    -0.5,   0.5,
-0.5,   -0.5,   0.5,

使用上面这些的顶点数据生成Buffer供渲染使用。

function makeBuffer() {
  var triangle = [
      // Z轴上的平面
      -0.5,   0.5,    0.5,  
      -0.5,   -0.5,   0.5, 
      0.5,    -0.5,   0.5, 
      0.5,    -0.5,   0.5,  
      0.5,    0.5,    0.5,   
      -0.5,   0.5,    0.5, 
      -0.5,   0.5,    -0.5, 
      -0.5,   -0.5,   -0.5,
      0.5,    -0.5,   -0.5,
      0.5,    -0.5,   -0.5, 
      0.5,    0.5,    -0.5,  
      -0.5,   0.5,    -0.5,
      // X轴上的平面
      0.5,    -0.5,   0.5,
      0.5,    -0.5,   -0.5,
      0.5,    0.5,    -0.5,
      0.5,    0.5,    -0.5,
      0.5,    0.5,    0.5,
      0.5,    -0.5,   0.5,
      -0.5,   -0.5,   0.5,
      -0.5,   -0.5,   -0.5,
      -0.5,   0.5,    -0.5,
      -0.5,   0.5,    -0.5,
      -0.5,   0.5,    0.5,
      -0.5,   -0.5,   0.5,
      // Y轴上的平面
      -0.5,   0.5,    0.5,  
      -0.5,   0.5,    -0.5,  
      0.5,    0.5,    -0.5,  
      0.5,    0.5,    -0.5,  
      0.5,    0.5,    0.5,  
      -0.5,   0.5,    0.5,  
      -0.5,   -0.5,   0.5,
      -0.5,   -0.5,   -0.5,
      0.5,    -0.5,   -0.5,
      0.5,    -0.5,   -0.5,
      0.5,    -0.5,   0.5,
      -0.5,   -0.5,   0.5,
  ];
  buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangle), gl.STATIC_DRAW);
  return buffer;
}

我移除了modelMatrix2,将modelMatrix1改为modelMatrix。每一次render的时候我将modelMatrix的赋值修改为围绕1,1,1轴旋转。

window.onWebGLRender = function render(deltaTime, elapesdTime) {
   ...
  var varyingFactor = (Math.sin(elapsedTime / 1000) + 1) / 2.0; // 0 ~ 1

  var rotateMatrix = mat4.create();
  var translateMatrix = mat4.create();
  mat4.rotate(rotateMatrix, rotateMatrix, varyingFactor * Math.PI * 2, vec3.fromValues(1, 1, 1));
  mat4.translate(translateMatrix, translateMatrix, vec3.fromValues(0, 0, -0.5));
  mat4.multiply(modelMatrix, translateMatrix, rotateMatrix);

  ...
}

最后在onWebGLRender中绘制正方体。

window.onWebGLRender = function render(deltaTime, elapesdTime) {
  ...
  var varyingFactor = (Math.sin(elapsedTime / 1000) + 1) / 2.0; // 0 ~ 1

  var rotateMatrix = mat4.create();
  var translateMatrix = mat4.create();
  mat4.rotate(rotateMatrix, rotateMatrix, varyingFactor * Math.PI * 2, vec3.fromValues(1, 1, 1));
  mat4.translate(translateMatrix, translateMatrix, vec3.fromValues(0, 0, -0.5));
  mat4.multiply(modelMatrix, translateMatrix, rotateMatrix);

  // 设置投影和观察矩阵
  var projectionMatrixUniformLoc = gl.getUniformLocation(program, 'projectionMatrix');
  gl.uniformMatrix4fv(projectionMatrixUniformLoc, false, perspectiveProjectionMatrix);
  var cameraMatrixUniformLoc = gl.getUniformLocation(program, 'cameraMatrix');
  gl.uniformMatrix4fv(cameraMatrixUniformLoc, false, cameraMatrix);

  var modelMatrixUniformLoc = gl.getUniformLocation(program, 'modelMatrix');
  gl.uniformMatrix4fv(modelMatrixUniformLoc, false, modelMatrix);
  gl.drawArrays(gl.TRIANGLES, 0, 36);
}

为了使正方体绘制正常,还有一个很重要的配置代码gl.enable(gl.DEPTH_TEST);。如果不调用它,绘制的结果是这样的。你会发现像素的前后顺序有些不对。

为什么会这样呢?默认情况下,谁后绘制谁在最上面,而gl.enable(gl.DEPTH_TEST);之后;绘制顺序则按照Z轴的坐标为准。为了配合gl.enable(gl.DEPTH_TEST);,还要将gl.clear(gl.COLOR_BUFFER_BIT);改成gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);,因为按照Z轴排序是要激活一个新的深度缓存区的,所以每次清除缓存区的时候不仅要清除颜色缓存数据还要清除深度缓存数据,否则会影响新的深度数据的写入。修改了两处之后,效果如下。

这次就正常了。本文主要介绍了绘制正方体的思路和方法,读者可以拓展一下,比如绘制由4个三角形构成的三角锥。立体几何体基本都可以分解成很多三角形,只要找到规律,绘制起来就很简单。

下一篇开始讲解基本的光照模型,让立方体看起来更真实。

Updated: