在前面知道了怎么将一个三角形显示到屏幕上,那么如果有很多三角形,各自距离相机的远近也不一样,并且三角形会相互遮挡。也就是三维空间中有很多物体,通常近处的物体会遮挡住远处的物体,那么在计算机渲染中该如何处理呢?
1.画家算法
灵感来至画家的绘画方式,这里的画是指油画,作画的时候先将远处的物体画在画布上,然后画中间的,最后才画最近的物体,近处画的物体会覆盖远处的物体。绘画完成后,会呈现出正确的遮挡关系,这就是画家算法(Painter's Algorithm)。
以计算机绘制立方体为例说明一下,绘制时,先绘制背后的面,接着绘制上下左右四个面,最后绘制前面的面,最终立方体呈现在屏幕中,遮挡关系符合预期。如下图左到右顺序所示:
上图中上下左右四个面距离相机是一样的,先从左面开始,然后上面,刚好上面挡住了左面是正确的。但是,有个问题,如果先从上面开始再左边绘制,左面挡住了上面就不对了。所以画家算法只在一定程度上是可以的。
画家算法虽然可以正确处理物体的遮挡关系,但是需要绘制之前先得到物体的远近关系,需要耗费一定的性能,这个问题是可以解决的,但是有一种特殊的情况画家算法无法正确处理。如下图所示:
这种情况比上面提到的立方体先画上面再画左面的情况更复杂。如上图中所示有三个三角形互相遮挡,此时画家算法无法准确分辨谁在前面谁在后面,无法使用画家算法在计算机中绘制出正确的遮挡关系。
既然以三角形(物体)为整体无法准确判断物体的遮挡关系,那可以更细一步,以屏幕中的像素为单位来判断物体的遮挡关系。这就是深度缓存(深度缓冲)或者Z-buffer。
2.深度缓存
在渲染出图像的同时,会生成另外一个图像,这个图像只存每个像素当前几何物体的最小z 值(最近)信息,这个图像叫做深度图(Depth Map) ,也叫深度缓存(z-buffer) 。而存储像素颜色的图叫做帧缓冲区(Frame buffer) 。这里为了简单,假设z 值总是正的,z 值越小物体越近,z值越大物体越远。如下图所示,左侧是渲染的图,右侧是深度图(值越小越黑),两张图都是同时生成的。
2.1深度缓存的实现方法
1.创建一个二维数组zBuffer[width][height],用来存放每个像素的深度,每个元素初始为一个比场景中最远物体的还大的z值(无限远)。
2.遍历每个物体覆盖的每个像素,将映射到该像素上每个物体的z值跟zBuffer对应深度作比较,小于zBuffer对应的深度则将该值更新为新的zBuffer深度。
代码如下:
举个例子。如下图所示,一开始深度缓存中每个像素深度存无限大的值(R),将第一个红色三角形画到上面,每个像素深度为5(小于R),所以将红色三角形画到上面,并且更新像素深度缓存值。接下来蓝色三角形里的每个像素深度值与深度缓存中的深度值做对比,如果小于深度缓存中的值,那么更新对应像素深度值并且将蓝色画到上面,否则不变。
2.2算法复杂度
在深度缓存中,假设有n 个三角形,都覆盖常数项个像素,那就就是常数乘以三角形个数的复杂度,也就是**O(n)**的算法复杂度。
但是怎么可能在线性时间内对n 个三角形排序呢?就像前面画家算法对n 个三角形排序,理论上对一组数据排序至少是O(n log(n)) 时间复杂度。因为深度缓存算法中并没有对n个三角形进行排序,只是在记录当前像素看到的最小值。
2.3注意事项
2.3.1顺序与z-buffer冲突
在之前的例子中,假如先画蓝色三角形,再画红色三角形,得到的结果是一样的,也就是说深度缓存算法与物体先后顺序无关。
那如果两个三角形在同一个像素上存在相同的深度缓存值呢(z-buffer冲突)?在图形学中,深度值一般是计算出的浮点型,两个浮点型相同基本上是不存在的,所以这里直接假设不存在两个三角形存在同一个像素里有相同的深度值。在实际3D引擎开发中深度重叠会出现闪面,需要手动调整物体位置。
2.3.2MSAA抗锯齿时深度缓存
采用MSAA抗锯齿时,深度缓存是针对每个采样点,不在是针对像素进行深度比对,默认是每个像素保存一个深度,如果做MSAA抗锯齿时时2*2的盒子,那么每个像素需要保存4个深度值。
2.3.3深度缓存无法处理透明物体
因为透明物体可以透过近处的物体看到远处的物体,所以不能通过深度值去判断,透明物体需要另外特殊的处理,不在本章讨论中。