SoRoMan

人若无名,便可专心练剑.

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  12 随笔 :: 1 文章 :: 41 评论 :: 0 Trackbacks

感觉很多书上都没讲清楚透视投影变换的推导过程,自己推导了下,以前一直含糊的关于方形/非方形的视平面和屏幕的宽高比的问题也有了答案.本文组织如下:

1.相机空间到视平面的变换
2.视平面到屏幕的变换
3.综合
4.一般情形

1.相机空间到视平面的变换


                       * p (xc,0, zc)
                      / |
                     /  |
                    /   |
           X    |/     |
           ^     *p' |(xp,0,zp)
           |   / |      |
           |  /  |      |
           | /   |      |
C(cam) |/    |      |
--------*----|----*------------->Z
           0    dx   zc
     (X-Z平面的投影示图)

a.透视投影一般的视景体为棱台,相机空间的物体会投影到视平面z=d,这里考虑左手坐标系,矩阵使用行优先方式。如图所示,由相似三角形知识可知相机空间中的物体投影到视平面上的坐标为:

xp = xc*(dx/zc)
yp = yc*(dy/zc)

其中,xc,yc,zc为相机空间坐标,xp,yp,zp为视平面坐标,dx,dy为x,y轴向的视距view distance,视平面到camera的距离,
故相机空间投影到视平面上的矩阵Tcp为:

|dx 0  0 0  |

|0  dy 0 0  |

|0  0   1 1  |

|0  0   0 0  |

(验证:Tcp右乘点p(xc,yc,zc,1)得点p'(xc*dx, yc*dy, zc, zc),转换为3D坐标为(xc*dx/zc, yc*dy/zc, 1),正确。)

********************************************************************
注:因为转换过程中点使用的是4D齐次坐标,所以最后需转换为3D坐标。4D齐次坐标(x,y,z,w)转换为3D坐标的方法为除以w分量,即对应3D坐标为(x/w,y/w,z/w)。
********************************************************************


考虑dx/zc和dy/zc项,如果dx != dy,则投影后x,y的比例会发生变化(原因:投影前坐标比例为xc/yc,投影后为xp/yp = xc*(dx/zc)/yc*(dy/zc) = xc*dx/yc*dy),从而投影后的图像的x,y比例会发生变形。

---------------------------------------------
结论1:所以,一般都会令d=dx=dy,即x,y向的视距相同。否则,图像失真。
---------------------------------------------

考虑视角(view angle,或视野filed of view)的问题,视角的大小不会影响到物体投影后的坐标,只会影响可视的范围。

在视距一样的情况下,x,y轴的视角可以不一样。如果一样,那么视平面就是一个正方形的。于是有:

tan(theta_x/2) = width_p/d
tan(theta_y/2) = height_p/d 

其中,theta_x,theta_y为x,y轴向的视角,width_p,height_p为视平面z=d的宽度(x轴)和高度(y轴)。
----------------------------------------------------------------
结论2:视平面的宽高比rp=width_p/height_p = tan(theta_x/2)/tan(theta_y/2)。
----------------------------------------------------------------

2.视平面到屏幕的变换

下面就是视平面到屏幕的变换了,这是一个2D到2D的变换(视平面的坐标需伸缩以填满整个屏幕空间,即在视平面中出现的所有的点要变换到屏幕上去,同时x,y轴向的比例还要维持和变换前一样,以免比例失真,同时视平面的坐标原点和屏幕中心点(x0=(width_s)/2, y0=(height_s)/2)对应),其实,就是一个坐标缩放加平移的过程:

xs = xp*kx + x0
ys = -yp*ky + y0
 
矩阵Tps为:

|kx     0      0 0  |

|0      -ky    0 0  |

|0      0       1 0  |

|x0   y0      0 1  |

(验证:Tps右乘点p(xp,yp,zp,1)得点p'(xp*kx + x0, -yp*ky + y0, zp, 1),转换为3D坐标为(xp*kx + x0, -yp*ky + y0, zp),正确。)

其中,kx,ky(kx>0,ky>0)为x,y轴向的缩放因子(kx=(width_s)/(width_p), ky = (height_s)/(height_p),和视距相同,kx,ky的值必须一样,否则图像的x,y比例又会发生变形。这里-yp*ky是因为一般屏幕的y轴是向下的,跟相机空间和视平面坐标系中的y轴方向相反。
------------------------------------------------------------------------------------------------
结论3: 一般令k=kx=ky,即x,y向的缩放因子相同。否则,图像失真。
于是有,width_s/width_p = height_s/height_p,变化等式得,rp = width_p/height_p = width_s/height_s = rs
所以,在x,y轴视距一样的情况下,要想最后变换到屏幕上的图像x,y比例不失真,必须rp=rs,即视平面的宽高比和屏幕的宽高比一样。

-----------------------------------------------------------------------------------------------

********************************************************************
注:若屏幕宽高为W,H,当W != H,即屏幕不为方形的时候,要确保投影到屏幕上的图像x,y比例不失真,则x,y轴视角(或视野FOV)肯定不能相等。
原因: 由结论2,3知,rp=width_p/height_p = tan(theta_x/2)/tan(theta_y/2)=width_s/height_s=rs=W/H。 故由W/H != 1 => theta_x != theta_Y.
********************************************************************

3.综合:

相机空间点p转换到屏幕空间点p',变换公式为:

xs = xc*(dx/zc)*kx + x0 = xc*(d/zc)*k + x0
ys = -yc*(dy/zc)*ky + y0 = -yc*(d/zc)*k + y0

综合变换矩阵(相机空间到屏幕空间)Tcs为:
 
Tcs = Tcp*Tps =

|d*k    0       0 0  |

|0      -d*k    0 0  |

|x0     y0      1 1  |

|0      0         0  0|

 其中,d为视距,k为屏幕宽高比或视平面宽高比,x0,y0为屏幕中心,注:最后需转换为3D坐标。

(验证:Tcs右乘点p(xc,yc,zc,1)得点p'(xc*d*k + x0*zc, -yc*d*k + y0*zc, zc, zc),转换为3D坐标为(xc*(d/zc)*k + x0, -yc*(d/zc)*k + y0, 1),正确。)

4.一般情形:
 ************************************
视距为1,x轴视角为90度,屏幕宽高为W,H.
************************************
 
代入d=1,theta_x = PI/2,x0= W/2,y0=H/2,则视平面宽高为width_p = 2。
要确保屏幕上的图像x,y比例不失真,即rs=rp,有
height_p = 2/rp=2/rs=2H/W,
k=kx=ky=width_s/width_p = W/2.

于是,矩阵为:

Tcs1 = 

|W/2    0       0 0  |

|0      -W/2    0 0  |

|W/2    H/2   1 1  |

|0      0         0 0  |



   |W/2    0                  0 0  |

|0      -H/2*(W/H)    0 0  |

|W/2    H/2              1 1  |

|0      0                    0 0  |

(可以看到,y轴的缩放因子中乘上了宽高比(aspect ratio))
 这个矩阵较常用。

---------------------
有什么问题,欢迎探讨.
 

posted on 2006-09-17 00:34 SoRoMan 阅读(8289) 评论(4)  编辑 收藏 引用

评论

# re: 探讨:3D透视投影变换详解-兼谈视平面和屏幕的宽高比问题 2007-03-29 12:10 heihei
Can you give you commons ,why it is  回复  更多评论
  

# re: 探讨:3D透视投影变换详解-兼谈视平面和屏幕的宽高比问题[未登录] 2007-03-29 12:13 a
http://www.chinasoft.org.cn/Html/mm/05155655.html  回复  更多评论
  

# re: 探讨:3D透视投影变换详解-兼谈视平面和屏幕的宽高比问题 2008-05-14 11:29 啊夏
hi SoRoman:
在你的博客上拜读了你的大作"探讨:3D透视投影变换详解-兼谈视平面和屏幕的宽高比问题".受益良多.

我按照你文章里面描述的.自己推导了一个公式.
世界坐标系的建立是这样的. x左向右.y上向下,z朝向屏幕里面.
在里面有一点 p(x,y,z);
设视点v (0,0,vz); 然后采用透视投影将p投在 xoy平面上.

我的公式是这样的.

t = -z/vz;
x' = t*x;
y' = t*y;
然后直接将 点(x',y') 绘制到 xoy屏幕上.

现在我遇到的问题是,在对线采用这样的方式进行投影的时候会有飞线的情况发生.(我是在一个地图引擎中使用). 在地图级别比较小的情况下,绘制还算正确.但是如果地图一放大线就乱了. 郁闷了好长时间了,希望能得到你的回信. 我的MSN: ren543#hotmail.com   回复  更多评论
  

# re: 探讨:3D透视投影变换详解-兼谈视平面和屏幕的宽高比问题 2012-09-22 09:24 xwl
tan(theta_x/2) = width_p/d
tan(theta_y/2) = height_p/d

这是地方有点问题吧,应该是
tan(theta_x/2) = width_p/2d
tan(theta_y/2) = height_p/2d
  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理