CG笔记之一——透视投影
于是,投影事实上成了投影变换。投影变换需要满足两个基本要求[1]:
1.保持depth的位序;
2.将直线变换为直线。
在x和y方向仍根据投影方法定义(投射在摄像机平面上的坐标),而在z方向上,需要引入伪距离映射(pseudo-distance),数学上,透视投影的伪距离映射具有形式z' = A + B / z。假设z方向截断分别为n和f = 1 / rf,截断分别映射为pn和pf。于是列写方程式1:
pn = A + B / n (式1)
pf = A + B * rf (式2)
解得:
A = (n * rf - pf) / (n * rf - 1) (式3)
B = n * (pf - pn) / (n * rf - 1) (式4)
可以证明,相应的齐次坐标变换矩阵为:
[ d, 0, 0, 0;
0, d, 0, 0;
0, 0, A, B;
0, 0, 1, 0] (式5)
一个很赏心悦目的方案是将n=d(目屏距)映射为0,而将rf=0(无穷远处)映射为1,这种情况下:A=1, B=-d。然而事实上,为了能够提供较好的depth分辨效果,n和f的选择(尤其是f)非常重要,f必须能容纳所有需要关心的物体,需要足够大,但是过大的话会导致depth分辨率的相对降低;而pn和pf则主要取决于为depth分配的位数,通常,现代显卡为depth配置了32位定点数。而透视投影的伪距离映射的一个较好的性质是,它对较近的物体提供了较大的分辨率,较远的物体则即使由于分辨率低而发生错误也是可以接受的。
然而在OpenGL中,用一个锥形顶点在圆点,截平面与z = 0平面平行的平截头体(frustum)向任意矩形映射的方式定义透视投影。它是一种更一般的透视投影形式。
列写关系式:
px = kx * x / z + dx (式6)
py = ky * y / z + dy (式7)
pz = kz / z + dz (式8)
可以导出齐次线性变换矩阵为:
P = [ kx, 0, dx, 0;
0, ky, dy, 0;
0, 0, dz, kz;
0, 0, 1, 0] (式9)
注意到:
M = [ 1, 0, 0, dx;
0, 1, 0, dy;
0, 0, 1, dz;
0, 0, 0, 1]
A = [ kx, 0, 0, 0;
0, ky, 0, 0;
0, 0, 0, kz;
0, 0, 1, 0]
有P = M * A (式10)
即P变换矩阵由投影(A矩阵)和屏幕平移(M矩阵)两部分变换组成。
根据映射关系:
(x0, ?, z0) -> (px0, ?, pz0), (x1, ?, z0) -> (px1, ?, pz0) (式11)
(?, y0, z0) -> (?, py0, pz0), (?, y1, z0) -> (?, py1, pz0) (式12)
(?, ?, z0) -> (?, ?, pz0), (?, ?, z1) -> (?, ?, pz1) (式13)
可以解得:
kx = z0 * (px1 - px0) / (x1 - x0), dx = (x1 * px0 - x0 * px1) / (x1 - x0) (式14)
ky = z0 * (py1 - py0) / (y1 - y0), dy = (y1 * py0 - y0 * py1) / (y1 - y0) (式15)
kz = z0 * (pz0 - pz1) / (1 - z0 / z1 ), dz = (pz1 - pz0 * z0 / z1) / (1 - z0 / z1) (式16)
注意到,kz是小于0的,这使得式8给出的伪距离映射是合法的。
注释
1 本笔记采用传统的z轴,即从眼睛指向屏幕,x从左至右,y从上至下(符合光栅扫描顺序),因此它仍是右手系的。
参考文献
[1] 3-D Computer Graphics, A Mathematical Introduction with OpenGL, Samuel R. Buss