透视和正交投影

上一篇介绍了变换矩阵,本篇将介绍两个重要的变换矩阵,透视投影矩阵和正交投影矩阵,下面是代码演示。

基本概念

透视投影矩阵

主要作用是模仿人眼观察3D世界的规律,让物体近大远小,所以被称为透视。

正交投影矩阵

主要作用是将坐标系映射到其他大小,主要用于2D UI绘制。

接下来我们就结合代码和效果深入了解这两个矩阵。我沿用了上一篇的代码,在渲染代码出根据当前选择的投影矩阵currentProjectionMatrix使用透视或者正交投影,并配合一些平移,旋转实现本例的效果。triangleBuffer中修改了顶点数据,将绘制的三角形改成了矩形,方便观察效果。同时记的把绘制的代码顶点数改成6,gl.drawArrays(gl.TRIANGLES, 0, 6);

window.onWebGLRender = function render(deltaTime, elapesdTime) {
  gl.viewport(0, 0, canvas.width, canvas.height);
  gl.clearColor(1.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);

  ...

  var rotateMatrix = mat4.create();
  mat4.rotate(rotateMatrix, rotateMatrix, elapesdTime / 1000.0, vec3.fromValues(0, 1, 0));

  var translateMatrix = mat4.create();
  mat4.translate(translateMatrix, translateMatrix, vec3.fromValues(0, 0, -1));

  var transform = mat4.create();
  mat4.multiply(transform, translateMatrix, rotateMatrix);
  mat4.multiply(transform, currentProjectionMatrix, transform);

  transformUniformLoc = gl.getUniformLocation(program, 'transform');
  gl.uniformMatrix4fv(transformUniformLoc, false, transform);

  gl.drawArrays(gl.TRIANGLES, 0, 6);
}
function makeBuffer() {
  var triangle = [
    -0.5, 0.5, 0.0, 
    -0.5, -0.5, 0.0,
    0.5, -0.5, 0.0,
    -0.5, 0.5, 0.0,
    0.5, -0.5, 0.0,
    0.5, 0.5, 0.0,
  ];
  buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangle), gl.STATIC_DRAW);
  return buffer;
}

透视投影

这是构建透视投影矩阵的代码。

perspectiveProjectionMatrix = mat4.create();
mat4.perspective(perspectiveProjectionMatrix, 80 / 180.0 * Math.PI, canvas.width / canvas.height, 0.1, 1000);

perspective方法有4个参数float fovyRadians, float aspect, float nearZ, float farZfovyRadians表示视角。aspect表示屏幕宽高比,为了将所有轴的单位长度统一,所以需要知道屏幕宽高比多少。nearZ表示可视范围在Z轴的起点到原点(0,0,0)的距离,farZ表示可视范围在Z轴的终点到原点(0,0,0)的距离,nearZfarZ始终为正。下面是透视投影的剖面示意图。

透视投影矩阵默认的可视方向是向Z轴的反方向生长的。视角(fovyRadians)越大,看到的东西就越多。只有在nearZ和farZ两个平面范围内的点才会被投影到屏幕上,当然这些点也必须在视角的范围内。根据上面的条件,一个位于z=0上的点是不能被投影到屏幕的,所以我在渲染时增加了一个平移矩阵 mat4.translate(translateMatrix, translateMatrix, vec3.fromValues(0, 0, -1));,为了演示近大远小的视觉效果,我又增加了旋转矩阵 mat4.rotate(rotateMatrix, rotateMatrix, elapesdTime / 1000.0, vec3.fromValues(0, 1, 0));。最后将 perspectiveMatrix * translateMatrix * rotateMatrix的结果赋值给Vertex Shader中的transform。 如果读者还记得上一篇提到的矩阵运算的话,就应该知道perspectiveMatrix * translateMatrix * rotateMatrix代表着先旋转再平移最后执行透视投影。这是以后所有3D变换操作的基本顺序,投影必须放在最后。透视投影的效果如下。

正交投影

构建矩阵的代码如下

orthoProjectionMatrix = mat4.create();
mat4.ortho(orthoProjectionMatrix, -0.8, 0.8, -0.8, 0.8, -100, 100);

正交投影其实比较好理解,原先屏幕的X轴从左到右是-1到1,Y轴从上到下是1到-1,经过mat4.ortho(orthoProjectionMatrix, -0.8, 0.8, -0.8, 0.8, -100, 100);正交矩阵的变换,就会变成X轴从左到右是-0.8到0.8,Y轴从上到下是0.8到-0.8。正交投影里的nearZ和farZ代表可视的Z轴范围,超出的点就不可见了。代码效果如下。

矩阵切换

例子通过select元素切换投影效果。根据不同的选择,currentProjectionMatrix会赋予不同的投影矩阵值,然后在渲染代码中直接使用。

function setupProjectionMatrixSelect() {
  currentProjectionMatrix = perspectiveProjectionMatrix;
  currentBuffer = triangleBuffer;
  var selectNode = document.getElementById("projectionModeSelect");
  selectNode.onchange = function (evt) {
    switch (evt.target.value) {
      case 'perspective': 
        currentProjectionMatrix = perspectiveProjectionMatrix;
        break;
      case 'ortho': 
        currentProjectionMatrix = orthoProjectionMatrix;
        break;
    }
  }
}

本篇主要介绍了WebGL中的两个重要投影矩阵,掌握好它们对于后面更深入的学习3D和2D渲染有着非常重要的作用。

下一篇会讲解摄像机,作为3D渲染中最基本的三大矩阵MVP之一,学习完之后就可以正式踏入3D渲染的世界了。

Updated: