WebGL Programing Guide 学习笔记
之前了解了一些 shader 的基础知识,现在终于可以进入激动人心的3D世界啦~
观察角度
不同于2D平面世界,在3D世界中我们需要一个观察角度。那么一个观察角度需要哪些变量确定呢?
显然,我们的眼睛坐标(eyeX eyeY eyeZ)和观察物体上的一个点坐标(atX atY atZ)可以构成一个向量:
但这个向量无法确定一个平面,试想我们转一转头,观察结果就发生了改变。因此还需要指定一个向上的向量:
在很多 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);