JavaScript WebGL 绘制顺序
引子
在 JavaScript WebGL 三维相关概念中尝试一些效果的时候,又碰到了新问题,就去查了资料,自己尝试后总结下。
绘制顺序
之前二维绘制顺序并没有产生明显的不好效果,现在绘制三维影响就比较大了。
先看看二维相同一套顶点不同顺序的效果:
两个面的形状和对应颜色始终保持一致,就是缓冲的顺序变了,发现 WebGL 默认情况下会按照缓冲区中顺序绘制,也就是后绘制的图形会覆盖先绘制的图形。这个就像一层一层贴纸一样,比较符合一般认知,所以在二维绘制时并没发现明显不对劲的地方。
关于三维绘制顺序先思考一下:三维的顶点坐标有三个分量,第三个分量是在 z 轴上表示深度信息,默认垂直屏幕向内为负方向,那么 z 值越大表示离屏幕越近,既然都有区分的依据,直接按照这个顺序绘制应该就可以了。实际试试:
示例 1 和示例 2 顶点和对应颜色都保持了一样,只有 z 分量和缓冲的顺序不同,如果是按照 z 分量区分的话,应该要保持一致才对,但发现示例 2 还是跟二维绘制面顺序一样,z 分量的设置并没有效果。查资料说要开启隐藏面消除功能:
gl.enable(gl.DEPTH_TEST)
再来看看示例 2 开启 DEPTH_TEST,发现还是没有效果,找到网上的一些示例并进行对比,发现还需要投影,才能看到效果,见示例 2 开启 DEPTH_TEST 且产生正确投影。
深度冲突
开启隐藏面消除功能之后,在绘制两个很靠近的面时,可能会出现显示异常的情况,这个现象称为深度冲突(Z fighting)。这里有两类这个现象的示例:
示例 1 的情况是一次绘制多个面,通过调整 z 分量比较容易解决。
示例 2 的情况是多次绘制多个面,不确定性相对更强,这个时候可以借助多边形偏移(polygon offset)来解决。在两次绘制之间指定偏移:
// 代码省略
gl.enable(gl.POLYGON_OFFSET_FILL);
gl.drawArrays(gl.TRIANGLES, 0, 3); // 开启多边形偏移
gl.polygonOffset(1, 1); // 指定偏移
gl.drawArrays(gl.TRIANGLES, 3, 3);
这是示例。
polygonOffset(factor, units) 指定加到每个顶点绘制 z 值上的偏移量,两个参数都是比例因子,最终值的计算公式是 factor × DZ + r × units ,其中 DZ 是顶点所在表面相对于观察者视线的角度, r 是设备能够解析偏移量的最小值。这个方法对于渲染隐藏线图像、将贴花应用于表面以及渲染具有高亮显示边缘立体图形很有用。