2 可见性与遮挡

2.1 画家算法

原理

先对远处的物体进行光栅化,后逐步对近处的物体进行光删化操作。(画近处的物体时会覆盖掉之前画的远处的一些物体)这样就可以正确地处理遮挡的问题了。

涉及到一个问题

这里涉及到一个问题:当某些物体在深度上存在互相遮挡的情况,此时没有办法定义顺序关系,这样就无法使用画家算法,因为不知道应该按照什么样的顺序画,或者不管先画什么都是错的。如下图所示:

2.2 问题解决方法:深度缓存/缓冲 z-buffer

思想:在生成最终图像的同时,也会生成一个“只存像素所看到的几何物体最浅的深度信息”的图像。然后利用深度缓存来维护遮挡信息。

  • 每一个像素内存储最浅深度值,即 z 值。

  • 需要额外的深度值缓冲区

    • 帧缓冲区(frame buffer)存储颜色值
    • 深度缓冲区(z缓冲区)存储深度

之前的讲述是摄像机永远看向z轴的负方向,因此所有的 z 都是负的。以为这 z 值越大距离反而越近,z 值越小,离摄像机的距离越远。

为了简化计算,将之前看到的 z 换一个概念。我们现在认为,摄像机到物体之间的距离,也就是深度信息,这个值永远都是正的。越小的值表示越近,值越大表示距离越远。

深度缓存算法思想:

  • 一开始将所有像素记录的深度缓存距离都初始化为无限远,即初始化为无限大的值。
  • 在光栅化的过程中,找到任意一个三角形所覆盖的任意一个像素。
  • 如果新的三角形要画在这个像素内,对于这个像素来说,如果新的 z 值比之前所存储的深度信息还要小,那么就将像素点的颜色值和深度信息值都进行更新。否则,什么都不做。

注意这里并未进行排序,只是在不断求最小值而已。复杂度为O(n)。

该算法有一个很重要的特点:和绘制顺序是没有关系的,只要正确求得最小的深度信息,最后绘制出来的图像就是对的。(前提条件,不出现在同一个像素点上出现相同的深度信息的情况。)

总结:

这是一个非常非常重要的算法,几乎广泛应用在所有的硬件中。每一个像素维护一个深度测试,就可以得到正确的遮挡算法。

之前有提到过 MSAA 的方法,对于这里面的采样点进行深度缓冲的话。意味着也不一定是对每一个像素进行 z-buffer, 也有可能是对采样点~

z-buffer 一定处理不了透明物体的深度,这个需要特殊处理。

绘制透明物体

缓冲区其实就是一张和最终输出图像分辨率一样大的图,每个像素存储了不同的数据。

1)深度缓冲区 z-buffer:每个像素记录了离相机最近的片元的深度。

2)颜色缓冲区 Frame buffer:也叫帧缓冲区,每个像素记录了当前该位置对应的颜色。

1、场景中存在半透明物体的正确渲染

​ 由于通过透明物体是可以看到被其遮挡的物体的,因此对于深度缓存中的深度值就不能直接替换更新,深度值应该是离相机最近的不透明物对应的片元的深度值,否则就会出现透明物体挡住不透明物体的渲染错误。 ​ 因此在渲染半透明物体时,需要关闭深度写入,但要保留深度测试。(深度测试的目的在于判断:是否因为遮挡而舍弃当前片元,如果该透明片元在不透明物体后,它被遮挡了,深度值比较大,当然应该被丢弃。当它在不透明物体前,它就不会被丢弃,但是又不会将当前的深度值写入,还是会以不透明物体的深度信息为准。)

​ 同时对于颜色也不可以和以前一样替换更新,而是要做透明度混合,即用帧缓存中已有的颜色和透明片元的透明色混合出一个新的颜色。

场景总存在透明物体时的渲染步骤:

  • 先渲染不透明物体,后渲染透明物体

  • 对透明物体进行排序,然后从远到进的顺序依次渲染。(因为只有这样才能正确地叠上,并混合出正确的颜色)

  • 并开启它们的深度测试,但关闭深度写入。

但此时还存在两个问题:

1)仍然无法解决透明物体相互交叠的问题。

2)透明物体内部如果自身互相交叠也会存在问题。无法判断片元的先后顺序。

2、半透明物体间相互交叠的问题

3、半透明物体的自身交叠问题及双面渲染

关闭背面剔除,将双面渲染的工作分成两个pass,渲染两次:

  • 第一个pass只渲染背面
  • 第二个pass只渲染正面

保证背面总是在正面被渲染之前渲染,从而可以保证正确的深度渲染关系