视点、目标点、上方向

我们看到的这个世界是怎么样的,取决于物体在哪儿,我们的眼睛在哪儿,还有头部的方向。我们处于世界坐标中,本身就有一个坐标。 你把头扭一下,看到的景象就不同了,甚至有可能超出可视范围,你看不到物体了。

把三维世界渲染到计算机的二维屏幕上,就像用照相机来拍照,最终拍出来的照片是二维的。照相机就是拍照的视点,上方向就是快门的方向, 照相机在不同的方向、角度来拍照,拍出的景象也不一样。而要被拍的物体,就是目标点了。 在 WebGL 中,我们需要一个视图矩阵,来描述视点、目标点、上方向等信息,最终把物体呈现到场景中。 它以旋转 Rotation 为基础,把相机旋转到一个点来拍照,跟相机不动,但物体沿相反角度旋转到一个点来拍, 效果是等效的。

<script id="vertexShader" type="x-shader/x-vertex"> attribute vec4 a_Position; uniform mat4 u_mvpMatrix; void main(){ gl_Position = u_mvpMatrix * a_Position; } </script>

旋转 Rotation 中的绕任意轴的旋转中的轴向量就是从原点(0, 0, 0)到点(x, y, z), 相机在(0, 0, 1)或 z>0 的位置上,上方向是(0, 1, 0),去看物体旋转。而现在,相机处于任意一个视点(eyeX, eyeY, eyeZ), 去看目标点(centerX, centerY, centerZ),上方向是(upX, upY, upZ),可以写一个函数来得到视图矩阵。这里统一采用 kanda and matsuda 的cuon-matrix.js,里面有个方法 setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ),可以返回一个矩阵来传给 u_viewMatrix,然后在 GLSL ES 中进行计算,得出 gl_Position。

如果物体也进行平移 Translation 缩放 Scale 旋转 Rotation 等变换,可在在 JavaScript 里把视图矩阵跟所有的变换矩阵相乘, 再赋给 GLSL ES 中的一个 uniform 变量。

正交投影 Orthographic Projection

此前的绘图效果都是在正交投影下展示的,物体的大小跟位置没有关系,只要它在可视空间内就可以了。 cuon-matrix.js 中有方法 setOrtho(left, right, bottom, top, near, far), Three.js 的构造函数是 OrthographicCamera( left, right, top, bottom, near, far ) { }

<script id="vertexShader" type="x-shader/x-vertex"> attribute vec4 a_Position; uniform mat4 u_viewMatrix; uniform mat4 u_projMatrix; void main(){ gl_Position = u_projMatrix * u_viewMatrix * a_Position; } </script> var projMatrix = new Matrix4(); projMatrix.setOrtho(-1.0, 1.0, -1.0, 1.0, 0.0, 2.0); var u_projMatrix = gl.getUniformLocation(gl.program, 'u_projMatrix'); gl.uniformMatrix4fv(u_projMatrix, false, projMatrix.elements); var viewMatrix = new Matrix4(); viewMatrix.setLookAt(eyeX, eyeY, eyeZ, 0, 0, 0, 0, 1, 0); var u_viewMatrix = gl.getUniformLocation(gl.program, 'u_viewMatrix'); gl.uniformMatrix4fv(u_viewMatrix, false, viewMatrix.elements);

透视投影 Perspective Projection

要做到跟我们日常生活一致,近大远小,实现真正的立体效果,就要用到透视投影了。 cuon-matrix.js 中有方法 setPerspective(fovy, aspect, near, far), Three.js 的构造函数是 PerspectiveCamera( fov, aspect, near, far ){ }。 其中 fov 是在垂直方向上的视角,aspect 是宽高比。

var projMatrix = new Matrix4(); projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); gl.uniformMatrix4fv(u_projMatrix, false, projMatrix.elements); var viewMatrix = new Matrix4(); viewMatrix.setLookAt(eyeX, eyeY, eyeZ, 0, 0, 0, 0, 1, 0);