【转】关于Z - Buffer的几篇文章

Z-Buffer 与 W-Buffer  

http://www.cnblogs.com/cofd/archive/2007/12/01/979473.html

 

 

Depth-Buffer(深度缓存)有两种:Z-Buffer 和 W-Buffer,这里讨论这两种深度缓存的区别,以及如何在两者之间转换。

w 的含义

3D空间点的坐标是(x,y,z),为了使矩阵乘法具有平移变换的功效,我们用4D空间中的点(x,y,z,w)来表示3D空间中的点(x',y',z'),这两个不同空间点之间的关系是:

    x' = x / w
    y' = y / w
    z' = z / w

像这样用四维空间点表示三维空间点,或者说用 n + 1 维空间点表示 n 维空间点的方法叫做 “齐次坐标表示法”。

实际使用中,在模型->世界转换、世界->视图转换过程中,w 通常保持不变,总是等于一,这样,齐次坐标的前三个分量就是对应3D空间点的三个坐标分量。但是,经过投影变换后,w 将得到一个比例值,比如,一般的透视投影变换矩阵是:

    | W   0   0   0 |
    | 0   H   0   0 |
    | 0   0   Q   1 |
    | 0   0  -QZn 0 |

    其中   Zn  =  近裁剪面 z 坐标
           Zf  =  远裁剪面 z 坐标
            W  =  2 * Zn / 视口宽度
            H  =  2 * Zn / 视口高度
            Q  =  Zf / (Zf - Zn)

将点(x,y,z,1)乘以此矩阵,w 便不再是一,而对应的3D空间点坐标(x / w,y / w,z / w)将出现一个缩放效果。同时,因为 w 的值通常与 z 坐标成正比(比如经过上面这个矩阵的变换,w 的值其实就是 z 坐标的值),所以经过投影变换,物体会产生近大远小的效果。

Z-Buffer 与 W-Buffer 的区别

简单的说,z-buffer 与 w-buffer 的区别就是前者保存的是点的 z 坐标,而后者保存的是点的 w 坐标。

具体的说,两者因为保存的值有不同的含义,所以表现出来的实际效果也会有差别。

z-buffer 保存的是经过投影变换后的 z 坐标,前面说过,投影后物体会产生近大远小的效果,所以距离眼睛比较近的地方,z 坐标的分辨率比较大,而远处的分辨率则比较小,换句话说,投影后的 z 坐标在其值域上,对于离开眼睛的物理距离变化来说,不是线性变化的(即非均匀分布),这样的一个好处是近处的物体得到了较高的深度分辨率,但是远处物体的深度判断可能会出错。

w-buffer 保存的是经过投影变换后的 w 坐标,而 w 坐标通常跟世界坐标系中的 z 坐标成正比,所以变换到投影空间中之后,其值依然是线性分布的,这样无论远处还是近处的物体,都有相同的深度分辨率,这是它的优点,当然,缺点就是不能用较高的深度分辨率来表现近处的物体。

从硬件实现角度来说,几乎所有的硬件3D加速卡都支持 z-buffer,而 w-buffer 的支持没有 z-buffer 那么广泛。另外,早期的 Direct3D 版本看起来也不支持 w-buffer。

Z-Buffer 与 W-Buffer 之间的转换

根据上面的矩阵变换,可以很容易的导出将 w-buffer 转换成 z-buffer 的公式:

    zDepth = Q * ( wDepth - Zn ) / wDepth
           = Zf / ( Zf - Zn ) * ( wDepth - Zn ) / wDepth

这个转换公式有什么用处?举个例子:3DS MAX 使用的是 w-buffer,如果从 3DS MAX 中导出深度信息到 Direct3D 中,作为预渲染的背景使用,就有可能用到上面这个转换。当然,如果在 D3D 中使用 w-buffer,问题就不大了,但是如果使用 z-buffer,不经过这样的转换,渲染结果就会出错。

 

 

浅析Z坐标、Depth Buffer和透视投影

这几个概念是3D图形学中很重要的内容,虽然基础,但对理解3D图形世界非常关键。所以了解透彻是很有好处和必要的。

下面先讲讲Z坐标。Z坐标和X、Y坐标一样。在变换、裁减和透视除法后,Z的范围为-1.0~1.0。DepthRange映射指定Z坐标的变换,这与用于将X和Y映射到窗口坐标的视口变换类似,但DepthRange映射又与视口映射有所不同,因为深度缓存的硬件方案对应用程序来说是隐藏的。调用DepthRange的参数是[0.0,1.0],与一片断相联的Z值(深度值)表示到眼睛的距离。在默认情况下,最接近眼睛的片断(在近截面上)被映射到0.0,离眼睛最远的片断(在远截面上)映射到1.0。片断可以映射为深度缓存范围的子集(通过在DepthRange中指定更小的值)。映射也可以相反,这样的话离眼睛最远的片断在0.0,最近的片断在1.0(调用DepthRange(1.0,0.0)),虽然这样反向映射是可以的,但对实际应用作用不大。

要理解为什么渲染质量上会不一致,最重要的是要理解屏幕Z坐标的特性。Z值指定了从片断到眼睛的距离。在正交投影中距离和Z值的关系是线性的,但在透视投影中却不是的。在透视投影中这种关系是非线性的,而且非线性的程度与Frustum函数中的far/near(或gluPerspective函数中的zFar/zNear)成比例。这种非线性在靠近近截面时增加了Z值的精度,而且增加了深度缓存的效率;但是在视见体的其它部分则降低了精度,也就减少了深度缓存的精确性。根据经验,far/near比值大于1000会有这种不好的效果。所以一般far/near比值应小于1000。要想解决这个问题,最简单的方法是通过将近截面远离眼睛来降低far/near比值,其唯一的副作用是离眼睛很近的物体可能会被裁减掉,但在特定的应用程序中这很少是个问题,近截面的位置对X、Y坐标的投影没有影响,因此这对图像的影响很小。

还有OpenGL光栅化和深度缓存的一些其他方面值得一提。一个大问题是光栅化过程使用不精确的算法,所以很难处理共面的图元,除非它们共享相同的平面方程。这个问题在有限精度的深度缓存实现中更加严重。这些问题包括:贴花(decaling)、隐藏线消除、轮廓多边形和阴影等。不过现在已经提出许多方法来解决这些问题,如PolygonOffset技术等。

深度缓存的位数是衡量深度缓存精度的参数。深度缓存位数越高,则精确度越高,目前的显卡一般都可支持16位的Z Buffer,一些高级的显卡已经可以支持32位的Z Buffer,但一般用24位Z Buffer就已经足够了。

PS: 光栅化插值时深度值是按1/Z来线性插值的。

 

 

 

posted on 2010-10-02 17:42  Once  阅读(526)  评论(0)    收藏  举报