WebGL Programing Guide 学习笔记

之前了解了一些 shader 的基础知识,现在终于可以进入激动人心的3D世界啦~

观察角度

不同于2D平面世界,在3D世界中我们需要一个观察角度。那么一个观察角度需要哪些变量确定呢?

显然,我们的眼睛坐标(eyeX eyeY eyeZ)和观察物体上的一个点坐标(atX atY atZ)可以构成一个向量:

观察角度

观察角度

但这个向量无法确定一个平面,试想我们转一转头,观察结果就发生了改变。因此还需要指定一个向上的向量:

Up vector

Up vector

在很多 WebGL 工具库中,都会提供操作矩阵的便捷方法。现在我们就能很好的理解所需的参数含义。 例如使用类似 setLookAt() 方法得到一个变换矩阵:

// cuon-utils.js

Matrix4.setLookAt(eyeX, eyeY, eyeZ, atX, atY, atZ, upX, upY, upZ)

在默认情况下,眼睛坐标是原点(0,0,0),观察向量是 z 轴负向,而向上向量是 y 轴正向。

矩阵变换

观察矩阵和模型变换矩阵可以在 vertex shader 中计算:

gl_Position = u_ViewMatrix * u_ModelMatrix * a_Position;

也可以在 JS 中将这两个矩阵组合成一个,然后传递给 vertex shader:

var modelViewMatrix = viewMatrix.multiply(modelMatrix);

可视范围

人的视角大约 200 度。在 WebGL 中,不在视角范围内的内容也不会被显示。

为了确定可视范围,我们引入了平行投影和透视投影。 在真实世界中透视的存在让我们对景深有直观的认识,所以在 3D 游戏场景中,通常使用透视投影。 而在一些绘图软件场景中,通常使用平行投影。

〈projection matrix〉 × 〈view matrix〉 × 〈model matrix〉 × 〈vertex coordinates〉

平行投影

Matrix4.setOrtho(left, right, bottom, top, near, far)

平行投影

平行投影

有一点需要注意,如果这个视角矩形的宽高比与 <canvas> 设定的不一致,会造成形状的拉伸。比如显示在 1:1 宽高比的 canvas 上会造成宽度的拉伸:

projMatrix.setOrtho(-0.3, 0.3, -1.0, 1.0, 0.0, 0.5);

透视投影

Matrix4.setPerspective(fov, aspect, near, far)

透视投影

透视投影

深度检测

由于 WebGL 按 Buffer 中 vertex 顺序绘制,因此可能出现远处的形状绘制在了近处形状之上。 WebGL 为了处理这种情况,增加了 Hidden Surface Removal 功能,使用一个 depth buffer 存储物体深度信息。判断标准显然是根据在 z 轴上的位置,因此也叫做 z-buffer。

开启深度检测,并且在每一次绘制开始前清空 Buffer。之前会清除 color buffer,使用位运算执行两者:

gl.enable(gl.DEPTH_TEST);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);

那么我们很自然想到,如果两个形状 z 坐标一样呢?在 WebGL 中这种情况叫做 Z Fighting。我们需要给每个 vertex 在 z 轴上增加一点点偏移,这样就能完全避免这种情况:

gl.enable(gl.POLYGON_OFFSET_FILL);
gl.polygonOffset(1.0, 1.0);