《转》探讨:3D透视投影变换详解-兼谈视平面和屏幕的宽高比问题
原文:http://www.cnblogs.com/soroman/archive/2006/09/17/509598.html
感覺很多書上都沒講清楚透視投影變換的推導過程,自己推導了下,以前一直含糊的關于方形/非方形的視平面和屏幕的寬高比的問題也有了答案.本文組織如下:
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向的視距相同。否則,圖像失真。
---------------------------------------------
于是,相機到投影平面的透視投影變化矩陣為
|d? 0 0 0 |
|0? d 0 0 |
|0? 0 1 1 |
|0? 0 0 0 |
============================
擴展話題1:這個矩陣是如何得出的?
============================
其實這里有個技巧: 根據方程組
xp = xc*(d/zc)
yp = yc*(d/zc)
zp = ?
原來變換矩陣應該是這樣的:Tcp=
|d/zc 0? 0 0 |
|0? d/zc 0 0 |
|0? 0??? k 0 |
|0? 0??? b 1 |
(其中,k,b 可以為任意值,因為投影變換后z值無所謂)
驗證:
轉換為齊次坐標:(xc yc zc 1)*Tcp = (x' y' z' w') = (xc*d/zc, yc*d/zc, k*zc+b, 1*1)
轉換為3D坐標: (x'/w' y'/w' z'/w') = (xc*d/zc, yc*d/zc, k*zc+b) ------(式1)
正確。
簡化1:
我們可以發現可以將zc放到齊次坐標轉換到3D坐標的時候考慮,即令w'=zc,這樣,
轉換為齊次坐標:(xc yc zc 1)*Tcp = (x' y' z' w') = (xc*d yc*d k*zc+b 1*zc)
轉換為3D坐標: (x'/w' y'/w' z'/w') = (xc*d/zc, yc*d/zc, (k*zc+b)/zc) -------可以看到
變換后x,y坐標和(式1)一樣,但是,矩陣Tcp就可簡化了:
|d 0? 0 0|
|0? d 0 0|
|0? 0 k 1|
|0? 0 b 0|
簡化2:將d考慮到齊次坐標轉換為3D坐標中去,令w'=zc/d
轉換為齊次坐標:(xc yc zc 1)*Tcp = (x' y' z' w') = (xc*1 yc*1 k*zc+b 1*zc/d)
這樣轉換為3D坐標: (x'/w' y'/w' z'/w') = (xc*d/zc, yc*d/zc, (k*zc+b)*d/zc) -------
可以看到變換后x,y坐標和(式1)一樣,但是,矩陣Tcp就可簡化了:
|1 0 0 0? |
|0 1 0 0? |
|0 0 k 1/d|
|0 0 b 0? |
特別地,令k=1,b=0,還可進一步簡化成:
|d? 0 0 0|
|0? d 0 0|
|0? 0 1 1|
|0? 0 0 0|
(此即上面使用的矩陣,此時z被變換為1/z)
以及:
|1 0 0 0? |
|0 1 0 0? |
|0 0 1 1/d|
|0 0 0 0? |
(此時z被變換為d/z)
===============================
擴展話題2:為什么DX/OGL中的投影矩陣這么復雜?
===============================
我們知道DX和OGL使用的投影矩陣要復雜的多。為什么呢,因為DX/OGL會將相機可見范圍(frustum)內所有的點經過
投影變換后線性映射到一個單位空間內,這個空間是:
DX:x={-1,1},y={-1,1},z={0,1}
OGL:x={-1,1},y={-1,1},z={-1,1}
(其中frustum為由right,left,top, bottom, near, far構成的一個可見空間-六面體,投影平面為near
平面,所以視距d = near)
經過投影變換Tcp=
|d 0? 0 0|
|0? d 0 0|
|0? 0 k 1|
|0? 0 b 0|
后,坐標轉換為(xc*d/zc, yc*d/zc, (k*zc+b)/zc),現在的問題就是如何將(xc*d/zc, yc*d/zc, (k*zc+b)/zc)線性映射到這個空間?
方法:
DX:
對于x,將x'= xc*d/zc從{left,right}線性映射到{-1,1}
簡單,構造線性映射x''=kx'+ b, 將邊界值代入得k=2*d/(r-l)=2*n/(r-l),b=-(r+l)/(r-l).
對于y,同理k=2*d/(t-b)=2*n/(t-b),b=-(t+b)/(t-b).
對于z,將z'=(k*zc+b)/zc從{near,far}映射到{0,1}
代入邊界值得k=f/(f-n),b=-f*n/(f-n)
所以最終矩陣Tcp(dx)=
|2*n/(r-l)???? 0??????????? 0????????? 0|
|0???????????? 2*n/(t-b)??? 0????????? 0|
|-(r+l)/(r-l)? -(t+b)/(t-b) f/(f-n)??? 1|
|0???????????? 0??????????? -f*n/(f-n) 0|
這個即是DX使用的投影矩陣。
============================
?
考慮視角(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:DX/OGL中的投影平面到屏幕的變換矩陣是怎樣的?
===============================
因為已經投影平面上的坐標已經映射到{-1,1},{-1,1},{0,1}/{-1,1}
所以kx=W/2,ky=H/2,x0=W/2,y0=H/2,z的變換無所謂。
Tps(dx) =
|W/2 0?????? 0 0? |
|0????? -H/2 0 0? |
|0????? 0??? 1 0? |
|W/2???? H/2 0 1? |?
?
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))
?這個矩陣較常用。
===============================
擴展話題4:DX/OGL中的相機到屏幕的變換矩陣和這個一致么?
===============================
Tcs(dx)=Tcp(dx)*Tps(dx)=
|W*n/(r-l)???? 0???????????? 0????????? 0|
|0???????????? -H*n/(t-b)??? 0???????? 0|
|-W*l/(r-l)??? -H*b/(t-b)??? f/(f-n)??? 1|
|0???????????? 0??????????? -f*n/(f-n) 0|
n=1,r=1,l=-1t=1,b=-1代入:
|W/2????????? 0????????? 0???????? 0|
|0??????????? -H*/2????? 0???????? 0|
|W/2????????? H*/2?????? f/(f-1)?? 1|
|0???????????? 0???????? -f*/(f-1) 0|
?
有什么問題,歡迎探討.
---------------------
posted on 2012-04-08 10:59 Wayne Cheng 閱讀(...) 評論(...) 編輯 收藏
轉載于:https://www.cnblogs.com/waynecheng/archive/2012/04/08/2437422.html
總結
以上是生活随笔為你收集整理的《转》探讨:3D透视投影变换详解-兼谈视平面和屏幕的宽高比问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中信信用卡开卡必须去银行吗
- 下一篇: 全域低风险什么意思