首页>
技术资讯>
详情

Z buffer 和 W buffer 簡介

2016-05-29 来源:CloudBest 阅读量: 362
关键词: 程序设计


  几乎所有目前的 3D 显示晶片都有 Z buffer 或 W buffer。不过,还是常常可以看到有人对 Z buffer 和 W buffer 有一些基本的问题,像是 Z buffer 的用途、Z buffer 和 W buffer 的差别、或是一些精确度上的问题等等。这篇文章的目的就是要简单介绍一下 Z buffer 和 W buffer。
  Z buffer 和 W buffer 是做什么用的呢?它们的主要目的,就是去除隐藏面,也就是 Hidden surface elimination(或是找出可见面,Visible surface detemination,这是同样意思)。在 3D 绘图中,只要有两个以上的三角面,就可能会出现某个三角面会遮住另一个三角面的情形。这是很明显的现象,因为近的东西总是会遮住远的(假设这些三角面都是不透明的)。所以,在绘制 3D 场景时,要画出正确的结果,就一定要处理这个问题。
  
  不过,这个问题是相当困难的,因为它牵扯到三角面之间的关系,而不只是某个三角面本身而已。所以,在做去除隐藏面的动作时,是需要考虑场景中所有的三角面的。这让问题变得相当的複杂。而且,三角面往往并不是整个被遮住,而常常是只有一部分被遮住。所以,这让问题变得更複杂。
  
  要做到去除隐藏面的最简单方法,就是「画家演算法」(Painter's algorithm)。这个方法的原理非常简单,也就是先画远的东西,再画近的东西。这样一来,近的东西自然就会盖住远的东西了。因为油画的画家通常会用这样的方法,所以这个方法被称为「画家演算法」。下图是一个例子:
  

 

  上图中,红色的圆形最远,所以最先画。然后是黄色的三角形,最后是灰色的方形。照远近的顺序来画,就可以达到去除隐藏面的效果。所以,只要把 3D 场景中的三角面,以对观察者的距离远近排序,再从远的三角面开始画,应该就可以画出正确的结果了。
  
  不过,实际上并没有这么理想。在 3D 场景中,一个三角面可能有些地方远,有些地方近,因为三角面有三个顶点,而这三个顶点和观察者的距离,通常都是不同的。所以,要以哪个顶点来排序呢?或是以三角面的中心来排序?事实上,不管以什么为依据来排序,都可能会有问题。下图是一个「画家演算法」无法解决的情形:
  
 

  上图中,三个三角面互相遮住对方,所以不管用什么顺序去画,都无法得到正确的结果。另外,这个方法也无法处理三角面有交叉的情形。
  
  当然,如果相当确定场景中不会出现这么奇怪的情形,那「画家演算法」一般还是可以用的。不过,它还有一个很大的问题,就是效率不佳。首先,画家演算法需要对场景中,在视角范围内所有的三角面做一个排序的动作。最好的排序演算法也需要 O(n log n) 的时间。也就是说,(大致上来说)如果三角面的数目从一千个变一万个,排序需要的时间会变成约 13.3 倍。而且,因为这需要对场景中所有的三角面来做,因此也不适合用特别的硬体来做加速。另外,这个方法还有一个很大的问题,就是它会花很多时间去画一些根本就会被遮住的部分,因为每个三角面的每个 pixel 都需要画出来。这也会让效率变差。
  
  如果场景是静态(不动)的,只有观察者会变动的话,那是有方法可以加快排序的速度。一个很常用的方法是 binary space paritioning(BSP)。这个方法需要事先对场景建立一个树状结构。建立这个结构后,不管观察者的位置、角度是如何,都可以很快找出正确的绘制顺序。而且,BSP 会视需要切开三角面,以处理像上图那样,三个三角面互相遮住对方的情形。
  
  不过,BSP 结构在建立时,要花很多时间,所以不太可能即时运算。因此,通常只能用在场景中的静态部分,而会动的部分还是需要另外排序。而且,BSP 常会需要切开三角面,也会让三角面的数目增加。另外,BSP 仍然无法解决需要画出那些被遮住的 pixel 的问题。
  
  另一种去除隐藏面的方法,是直接以 pixel 为单位,而不是以三角面为单位,来考虑这个问题。其中最简单的方法是由 Catmull 在 1974 年时提出来的,也就是 Z buffer(或称 depth buffer)。这个方法非常简单,又容易由特别设计的硬体来执行,所以在记忆体容量不再是问题后,就变得非常受欢迎。
  
  Z buffer 的原理非常简单。在绘制 3D 场景时,除了存放绘制结果的 frame buffer 外,另外再使用一个额外的空间,也就是 Z buffer。Z buffer 记录 frame buffer 上,每个 pixel 和观察者的距离,也就是 Z 值。在开始绘制场景前,先把 Z buffer 中所有的值先设定成无限远。然后,在绘制三角面时,对三角面的每个 pixel 计算该 pixel 的 Z 值,并和 Z buffer 中存放的 Z 值相比较。如果 Z buffer 中的 Z 值较大,就表示目前要画的 pixel 是比较近的,所以应该要画上去,并同时更新 Z buffer 中的 Z 值。如果 Z buffer 中的 Z 值较小,那就表示目前要画的 pixel 是比较远的,会被目前 frame buffer 中的 pixel 遮住,所以就不需要画,也不用更新 Z 值。这样一来,就可以用任意的顺序去画这些三角面,即可得到正确的绘制结果。下图是一个例子:
  
 

  上图中,红色的三角面虽然先画出来,但是因为使用了 Z buffer,所以后画的黄色方块还是只会遮住适当的部分,而不会连较近的部分都遮住。这就显示出 Z buffer 的效果。
  
  实际上 Z buffer 中能存放的数字当然会有一定的限度,所以通常会把 Z 值缩小到 0 ~ 1 的范围。因此,在绘制 3D 场景时,就会需要把可能出现的 Z 值限制在某个范围内。通常是用两个和投影平面平行的平面,把所有超出这两个平面范围的三角面都切掉。这两个平面通常分别称为 Z near 和 Z far,分别表示较近的平面和较远的平面。而在 Z near 平面的 Z 值为 0,在 Z far 的 Z 值为 1。
  
  在效率上 Z buffer 并不一定会比「画家演算法」要快。但是,它比较简单。而且,它的效率和三角面的数目并没有太大的关系,而是和绘制的 pixel 数目有关。所以,而且可以很容易设计出特定的 3D 硬体来做这个动作,而不需要由 CPU

热门推荐 查看更多