在这小节中,我们来看另一个Buffer:Depth Buffer(深度缓冲区),你将了解到:
- 深度缓冲区的作用和原理
- 深度缓冲区的用法
- 深度缓冲区的实际运用
作用和原理
在观察三维世界的时候,物体之间总会存在前后的遮挡关系,既然是3D渲染,那么渲染出的场景也应该和实际情况一样:靠近眼睛的物体会遮挡远离眼睛的物体。深度缓冲区就是用来实现这个效果的。
当启用深度检测的时候,WebGL会将深度信息(深度信息位于Z轴方向,所以也叫Z-Buffer)写入深度缓冲区,在绘制阶段便会根据缓冲区深度信息大小来决定物体的某个部分是否需要画出来。深度值小则距离眼睛近,深度值大则距离眼睛远。在深度检测过程中,物体被遮挡的部分通常是检测失败的,因此也就无需绘制出来,进而实现了前面物体遮挡后面物体的效果。
在了解深度检测和深度缓冲区原理之后,还可以利用它实现很多其他效果。
用法
前面提到,在使用深度缓冲区前一定要开启深度检测,这需要在初始化WebGL环境时确保depth属性为true(默认为true,确保没有设置为false就行了),并调用gl.enable方法开启(默认是关闭的):
gl.enable(gl.DEPTH_TEST);
通过gl.depthFunc可以指定深度检测的参数,即什么情况算失败、什么情况算作通过。默认为gl.LESS,意思是当前处理的像素深度值如果小于目前深度缓冲区的值则通过,显然这样做是为了让靠近眼睛的物体遮挡在远离眼睛的物体前。depthFunc允许的值如下所示:
- gl.NEVER (总不通过)
- gl.LESS(如果新值小于缓冲区中的值则通过)
- gl.EQUAL(如果新值等于缓冲区中的值则通过)
- gl.LEQUAL(如果新值小于等于缓冲区中的值则通过)
- gl.GREATER(如果新值大于缓冲区中的值则通过)
- gl.NOTEQUAL(如果新值不等于缓冲区中的值则通过)
- gl.GEQUAL(如果新值大于等于缓冲区中的值则通过)
- gl.ALWAYS(总通过)
在这个代码示例中,我们绘制一前一后两个三角形,开启深度检测后,前面的白色三角形会遮挡后面的黑色三角形。
写入深度信息中的值到底是什么?
深度信息描述的是物体距离视点的远近,那么这个数值具体怎么是怎么算出来的?是否有办法修改呢?
在介绍坐标转换的文章中我们提到视口变换的概念,它是将NDC坐标转换为屏幕坐标(或叫窗口坐标),你会发现NDC是三维的,而屏幕坐标是二维的,其实这里缺少了z方向的屏幕坐标(虽然屏幕上只有x、y)。z的屏幕坐标计算方式如下:
其中n和f是通过下面的函数指定的,默认n为0,f为1。
void depthRange(GLclampf zNear, GLclampf zFar)
由于NDC坐标中z的取值范围是[-1, 1],那么z的窗口坐标范围就变为[0, 1]。最终写入深度缓冲区并用来做比较的就是这个数。
通过depthRange也可以修改near和far的值,但是near和far必须在区间[0, 1],且near小于等于far。
near小于等于far