深度 Depth

上面两张图,是由下面两种不同的顶点数据而导致的。WebGL 里画点按照数组 Float32Array([ ]) 里的先后顺序,先给出的点先画, 这样就导致了在z轴上后面的跑到前面来。解决的方法是开启隐藏面消除 gl.enable( gl.DEPTH_TEST ),在任何三维场景中, 都应该开启隐藏面消除,但前提是要正确设置可视空间,否则就可能产生错误的结果。

verticesColors1 = new Float32Array([ 0.0, 1.0, -4.0, 0.4, 1.0, 0.4, // green -0.5,-1.0, -4.0, 0.4, 1.0, 0.4, 0.5, -1.0, -4.0, 1.0, 0.4, 0.4, 0.0, 1.0, -2.0, 1.0, 0.4, 0.4, // yellow -0.5,-1.0, -2.0, 1.0, 1.4, 0.4, 0.5, -1.0, -2.0, 1.0, 1.4, 0.4, 0.0, 1.0, 0.0, 0.4, 0.4, 1.0, // blue -0.5,-1.0, 0.0, 0.4, 0.4, 1.0, 0.5, -1.0, 0.0, 1.0, 0.4, 0.4 ]), verticesColors2 = new Float32Array([ 0.0, 1.0, 0.0, 0.4, 0.4, 1.0, // blue -0.5,-1.0, 0.0, 0.4, 0.4, 1.0, 0.5, -1.0, 0.0, 1.0, 0.4, 0.4, 0.0, 1.0, -2.0, 1.0, 0.4, 0.4, // yellow -0.5,-1.0, -2.0, 1.0, 1.4, 0.4, 0.5, -1.0, -2.0, 1.0, 1.4, 0.4, 0.0, 1.0, -4.0, 0.4, 1.0, 0.4, // green -0.5,-1.0, -4.0, 0.4, 1.0, 0.4, 0.5, -1.0, -4.0, 1.0, 0.4, 0.4, ]),

在调用 gl.drawArrays() 进行绘图之前,先清除深度缓冲区 gl.clear( gl.DEPTH_BUFFER_BIT ), 它可以跟清除颜色缓冲区合写 gl.clear( (gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT )。

深度冲突 Z fighting

var viewProjMatrix = new Matrix4(); viewProjMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); viewProjMatrix.lookAt(3, 2, 8, 0, 0, -5, 0, 1, 0); verticesColors = new Float32Array([ 0.0, 1.0, -2.0, 0.4, 0.4, 1.0, // blue -1.0,-1.0, -2.0, 0.4, 0.4, 1.0, 1.0, -1.0, -2.0, 1.0, 0.4, 0.4, 0.0, 2.0, -2.0, 1.0, 0.4, 0.4, // yellow -1.5,-2.0, -2.0, 1.0, 1.4, 0.4, 1.5, -2.0, -2.0, 1.0, 1.4, 0.4 ]),

这两个三角形的 z 坐标都一样,当它们的 x、y 坐标没有重叠时,是能够正常区分的。但有相叠在一起的部分时,就会产生深度冲突了。 因为两个表面过于接近,深度缓冲区的精度不能区分哪个在前,哪个在后了。WebGL 提供多边形偏移的机制来解决这个问题。它自动在 z 值上加一个偏移量,偏移量由物体表面相对于观察者视线的角度来确定。计算公式 m * factor + r * units 。 其中 m 表示顶点所在表面相对于观察者的视线的角度,而 r 表示硬件能够区分两个 z 值之差的最小值。

gl.enable( gl.POLYGON_OFFSET_FILL ); gl.polygonOffset( factor, units );

但实际上,开启多边形偏移也不一定能解决问题,这跟视角、<canvas> 的尺寸都有关系。下面截图,分别是对于上面代码的数据,在 <canvas> 的 widht、height 分别在 200、300、400、500、600 时的实际渲染效果。