CG笔记之一——透视投影

  以前以为投影也就投影了,并不了解投影也可视作坐标变换,而且也是一个齐次坐标(Homogeneous Coordinate)系的坐标变换。因此书中的介绍一开始也不明白——它说透视投影会把直线映射成曲线,还特地验证一下,发现投影为2D空间(摄像机平面)并不会发生这种情况,在仔细一看才知道,它指的是映射到3D空间,在z方向发生了畸变。而后逐渐了解,原来z方向的位置信息(depth)在渲染级模块处(所谓的Z-Buffer)有特殊用处(直线上每个点的depth均通过直线两端的点线性插值完成,因此可能造成depth位序异常)。当然这个问题在某些场合是不会发生的,例如完全基于光线跟踪算法的渲染并不是这样形成depth的。

于是,投影事实上成了投影变换。投影变换需要满足两个基本要求[1]
  1.保持depth的位序;
  2.将直线变换为直线。
  在x和y方向仍根据投影方法定义(投射在摄像机平面上的坐标),而在z方向上,需要引入伪距离映射(pseudo-distance),数学上,透视投影的伪距离映射具有形式z' = A + B / z。假设z方向截断分别为n和f = 1 / rf,截断分别映射为pnpf。于是列写方程式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

posted @ 2007-04-08 17:14  quanben  阅读(349)  评论(0编辑  收藏  举报