透视投影,与Z BUFFER求值

透视投影,与Z BUFFER求值
   
    为什么有透视。因为眼球是个透镜。假如地球生物进化的都靠超声波探测空间,那也许眼睛就不会有变成球,而是其他形状...
为什么有人玩3D头晕?其中一个重要的作用是,眼球不完全是个透镜。所以当视野大于60度,屏幕四周投影的变形就比眼球投影视网膜利害多。而且人脑习惯了矫正眼球投影的信息。突然有个屏幕上粗糙的模拟眼球成像,大脑还真一时适应不了。

    Z BUFFER数值计算,以及PERSPECTIVE PROJECTION MATRIX设置,使用D3D或者OPENGL,可以直接让显卡完成这些工作。但是弄清Z BUFFER如何计算,以及PROJECT MATRIX的原理。对于进行各种高级图像处理,非常有用。例如shadowmap的应用。目前为了得到好的shadowmap,很多人在如何加大shadowmap精度做了很多努力(改变生成shadowmap时的perspective project matrix来生成精度更合理的shadowmap) 。比如透视空间的perspective shadow map,light空间的Light-space perspective shadow,perspective shadowmap变种Trapezoidal shadow maps,改正交投影为对数参数投影的 Logarithmic Shadow Maps。另外,Doom3中shadow volume采用的无限远平面透视矩阵绘制stencil shadow volume。都需要对perspective projection有透彻了解。


以下描述z buffer计算以及perspective projection matrix原理。

假设坐标在 world space 是
Pw = {Xw,Yw,Zw}

经过camera space transform 得到
Pe = {Xe,Ye,Ze}

然后经过project transform 转为 device space,这里假设转为 Zp 范围 [-1,1](OPENG的Z BUFFER)

Pe在near平面的投影为:
Xep = n* Xe/(-Ze) (n为近平面到eye距离).
注意这里OPENGL 右手系camera space是Z轴负方向为眼睛看的方向。当计算投影时,x,y都应该除以一个正的数值。所以Ze取负。

这么做的目的是为了让在view space中所有在视锥平截体内的点,X数值投影到近平面上,都在near平面上的left到right。
接下来是求最后在device space里x,y,z的数值,device space是坐标为-1到1的立方体。其中x/2+0.5,y/2+0.5 分别再乘以窗口长宽就是屏幕上像素的位置。那么屏幕这个像素的Z数值就可以按以下方法求出:
需要把view space中(left,right,top,bottom,near,far)的frustum转换到长宽为(-1,1)的正方体内,就是说,把Xe,Ye,Ze都映射到[-1,1]的范围内。前面已经求出了Xe在camera space near平面上的投影Xep。这个投影的数值就是在frustum平截体near平面上的位置。
把平截体near平面映射到-1,1的正方形很简单,X方向按如下映射:
Xp = (Xep - left)*2/(right-left) -1  。

当Xep在left到right变化时,Xp在-1到1变化。
因为显卡硬件(GPU)的扫描线差值都是透视矫正的,考虑投影后,顶点Xp和Yp都是 1/(-Ze) 的线性关系。那么在device单位立方体内,Zp 的范围在[-1,1]之间,Zp也就是Z BUFFER的数值。根据前面推导,要保证透视校正,Zp也是以1/(-Ze)的线性关系。即,总能找到一个公式,使得
Zp = A* 1/(-Ze) + B。

也就是说,不管A和B是什么,在Z BUFFER中的数值,总是和物体顶点在camera space中的 -1/Ze 成线性的关系。 我们要做的就是求A 和B。
求A和B,我们利用以下条件:
当Ze = far 时, Zp = 1
当Ze = near时,Zp = -1(在OPENGL下是这样。在D3D下是Zp =0)
这实际是个2元一次方程。有了两个已知解,可以求出A和B。OPENGL下,
A = 2nf/(f-n), B = (f+n)/(f-n)

这样一来,我们就知道Z BUFFER的数值如何求得。先把物体顶点世界坐标Pw变换到camera space中得到Pe。然后再经过透视投影变换,求得Ze->Zp的数值。把这个数值填入Z buffer,就是显卡用来比较哪个像素在前,哪个像素在后的依据了。
这也就是为什么near和far设置不合适的时候,很容易发生z fighting。一般情况下,离屏幕很近的一段距离,已经用掉了90%的z 浮点精度。在用于渲染视角里中远距离的场景时,深度的判别只靠剩下的10%精度来进行。
具体推导可以看看http://www.cs.kuleuven.ac.be/cwis/research/graphics/INFOTEC/viewing-in-3d/node8.html 

逐渐被D3D抛弃的W BUFFER,场景远近与W数值是线性的。即,100米的距离,在near=1 far=101时,每一米在D3D W BUFFER的表示中,就是差不多 1/100 那么大。但是在Z BUFFER中就完全不是均匀分配。

下面考虑perspective projection matrix。
根据线性代数原理,我们知道无法用一个3x3的matrix对顶点(x,y,z)进行透视映射。无法通过一个3X3的矩阵得到 x/z 的形式。进而引进齐次坐标矩阵---4x4 matrix。顶点坐标(x,y,z,w)。
齐次坐标中,顶点(x, y, z, w)相当于(x/w, y/w, z/w, 1)。 看到这个顶点坐标,我们会联想到前面我们最终求出的z buffer数值Zp和单位device space中的Xp坐标。利用矩阵乘法,可以得到一个矩阵Mp,使得(Xe,Ye,Ze,1)的顶点坐标转换为齐次坐标规一化后的 (Xp,Yp,Zp,1) 。  即:
Vp = Mp * Ve  .
Vp是单位设备坐标系的顶点坐标(Xp,Yp,Zp,1)。Ve是camera space顶点坐标(Xe,Ye,Ze,1)。

考虑
Xp = (Xep - left)*2/(right-left) -1      (Xep  = -n* Xe/Ze)
Yp = (Yep - left)*2/(right-left) -1      (Yep  = -n* Ye/Ze)
Zp = A* 1/Ze + B

为了得到4X4 MATRIX,我们需要把(Xp,Yp,Zp,1)转为齐次坐标 (-Xp*Ze, -Yp*Ye, -Zp*Ze, -Ze) 。然后由矩阵乘法公式和上面已知坐标,就可以得到PROJECTION MATRIX。

Xp*(-Ze) = M0  M1  M2  M3                  Xe
Yp*(-Ze) = M4  M5  M6  M7        x         Ye
Zp*(-Ze) = M8  M9  M10 M11                 Ze
-Ze    = M12 M13 M14 M15                   1

这里拿 M0, M1, M2, M3 的求解来举例:
M0* Xe + M1* Ye + M2* Ze + M3= (-Ze)*(-n*Xe/Ze-left )*2/(right-left) +Ze
M1 = 2n/(right-left)
M2 = 0
M3 = (right+left)/(right-left)
M4 = 0

最后得到Opengl 的 Perspective Projection Matrix:

[ 2n/(right-left)   0                                  (right+left)/(right-left)    0                            ]
[ 0                 2*near/(top-bottom)                (top+bottom)/(top-bottom)    0                            ]
[ 0                 0                                  -(far+near)/(far-near)       -2far*near/(far-near)        ]
[ 0                 0                                  -1                           0                            ]

D3D 的左手系透视投影矩阵和OPENGL有以下区别。
1, D3D device space 不是个立方体,是个扁盒子。z的区间只有[0,1] 。x,y区间还是[-1,1]
2,D3D的camera space Z轴朝向正方向,计算camera space中投影时不用 Xep = n*Xe/(-Ze), 而是 Xep = n*Xe/Ze
3,D3D中,从camera space的视椎平截体到device space的单位体(扁盒子)摄影,采用了很奇怪的作法。把frustum右上角映射为device单位体的(0,0,0)位置

请RE D3D PERSPECTIVE PROJECTION MATRIX推导过程~

后面若有时间继续 透视校正texture mapping,phong shading以及bump mapping等per-pixel processing光栅化

posted @ 2008-07-05 04:32  puzzy3d  阅读(4418)  评论(6编辑  收藏  举报