投影矩阵的推导(转载)
轉載自kanegohttp://www.cnblogs.com/kanego/articles/2346971.html
[譯] - 投影矩陣的推導
原帖地址:
http://www.codeguru.com/cpp/misc/misc/math/article.php/c10123__1/Deriving-Projection-Matrices.htm
譯文:
矩陣變換作為3d圖形程序員的基本知識,投影矩陣是其中很復雜的內容。平移和縮放是容易理解的,旋轉矩陣只需要掌握了基本的三角幾何知識,但是投影 矩陣不一樣。如果你看過投影矩陣的形式,你會發現你很難很快知道它是怎么來的。而且,我在網上沒有發現很多有關推導投影矩陣的資源。本文就是講述如何推導 投影矩陣。
對于新接觸3d圖形學的人來說,推導投影矩陣需要有一定的數學基礎,但不是必須的。你可以直接使用最后的公式,如果你使用一種圖形API,如 Direct3D,通常你不需要關心,因為API已經提供了計算投影矩陣的功能。一旦你理解了如何推導它,這對你沒有壞處。這篇文章為了那些需要了解推導 投影矩陣細節的人的。
簡介:什么是投影
計算機顯示器是一個2d表面,為了顯示3d物體,你需要把3d的物體轉化成2d的圖片,這個過程就是投影。舉一個簡單例子,最簡單的把3d物體變到2d表面上的方法是去掉z坐標。對于1個立方體,如下圖1。
圖1:去掉z坐標投影到xy平面
當然,圖1方法很簡單,大多數情況不適用。這里不會投影到一個平面上,相反,這里講的投影會把物體變換到一個規范的是視空間(canonical view volume這個是叫什么?)。對于規范的視空間的坐標,不同圖形API可能不同。作為討論起見,這里使用Direct3D的,即(-1, -1, 0)到(1, 1, 1)。一旦物體的坐標轉換到了規范的視空間里面,其中的x、y坐標就用于映射到屏幕空間,z坐標一般用于z-buffer。
注意圖1使用了左手坐標系。這也是Direct3D的形式,本文將一直使用左手坐標系。對于右手坐標系,本文講述的知識都是適用的。
現在可以開始講投影變換了。這里主要講述2中最普遍的形式:正投影和透視投影。
正投影
正投影是一種簡單的投影形式,要求所有的投影射線垂直于投影面。正投影最終的規范視空間是一個AAB(axis-aligned box),如下圖2。
圖2:正投影
從圖中可以看出,規范的視空間是由6個面組成的:
左:x = l
右:x = r
下:y = b
上:y = t
近:z = n
遠:z = f
由于正投影的視錐(view volume)和規范視空間(canonical view volume)都是AAB,所以不會有像透視投影那樣隨距離變化的特性。對于正投影,所有的物體的大小不會變化,而且不會隨著距離變化。
圖3:簡單的正投影應用(原文里的圖 轉帖者添加)
下面開始推導正投影矩陣。一個在view volume中的點的x坐標在[l, r],這需要變換到canonical view volume中的[-1, 1]。
l <= x <= r
0 <= x-l <= r-l
0 <= (x-l)/(r-l) <= 1
0 <= 2(x-l)/(r-l) <= 2
-1 <= 2(x-l)/(r-l) - 1 <= 1
-1 <= (2x-r-l)/(r-l) <= 1
最后你需要得到px+q的形式,所以分解成:
-1 <= 2x/(r-l) - (r+l)/(r-l) <= 1
所以得到了在canonical view volume中的x坐標:
x' = 2x/(r-l) - (r+l)/(r-l)
同理得到在canonical view volume中的y坐標:
y' = 2y/(t-b) - (t+b)/(t-b)
最后來推導z'的公式。在view volume中的點的z坐標在[n, f],需要變換到canonical view volume中的[0, 1]。
n <= z <= f
0 <= z-n <= f-n
0 <= (z-n)/(f-n) <= 1
0 <= z/(f-n) - n/(f-n) <= 1
得到z'的表達式:
z' = z/(f-n) - n/(f-n)
總結上面的x', y'和z',
x' = 2x/(r-l) - (r+l)/(r-l)
y' = 2y/(t-b) - (t+b)/(t-b)
z' = z/(f-n) - n/(f-n)
如果寫成矩陣形式:
在Direct3D中一個函數D3DXMatrixOrthoOffCenterLH()正好提供了這個功能(注意到形式有些不同,是行/類矩陣的區別)。“LH”表示的是左手坐標系,但是“OffCenter”表示的又是什么呢?
第一,在Camera空間,如果Camera被放置在原點,并且方向沿著z軸;第二,如果r = -l,t = -b,并且定義x軸上的寬度w和y軸上的高度h,那么
上面的矩陣和Direct3D中的D3DXMatrixOrthoLH()計算的結果一致。
如果把正投影的矩陣分解成如下:
注意變換形式是:p'(canonical view volume) = P0 * P(view volume)。
通過這個分解,可以這樣來理解正投影矩陣,首先,吧view volume沿z軸把近平面移到原點,其次,縮放使得view volume變換到canonical view volume。
透視投影
透視投影因為可以產生距離的錯覺(遠方的物體看起來更小),所以可以產生更真實的效果,被廣泛使用。它和正投影不同的地方是,透視投影的view volume是一個錐體,如下圖4:
圖4:透視投影
從圖中可以看出view frustum的近平面由(l, b, n)延伸到(r, t, n)。
步驟一:對于view frustum中的一個點(x, y, z),把它投影到近平面z = n。因為 投影點在近平面上,所以x坐標的范圍在[l, r],y坐標的范圍在[b, t]。如 圖5
步驟二:使用在正投影中學習的知識,把在近平面上x的[l, r]隱射到[-1, 1],y的[b, t]隱射到[-1, 1]。
圖5
第一步:得到投影點
所以得到投影點(x*n/z, y*n/z, n)。
第二步:把第一步中得到的投影點,運用正投影中學習的知識隱射。
代入x = x*n/z 和 y = y*n/z,
乘以z,
把(x, y, z)隱射到(x'z, y'z, z'z),所以需要求z'z
假設z'z = pz + q,而z坐標的隱射是[n, f]到[0, 1],所以得到:
0 = pn + q
f = pf + q
解方程得:
p = f/(f-n)
q = -fn/(f - n)
所以,
z'z = fz/(f-n) - fn/(f-n)
總結在一起,
寫成矩陣形式:
上面的矩陣可以使得,p * (x, y, z, 1) = (x'z, y'z, z'z, z)。這個也是和Direct3D中的D3DXMatrixPerspectiveOffCenterLH()是一樣的(注意行/列區別)。同正投影中, 如果滿足一定的條件,r = -l,t = -b,則有下面的矩陣形式:
這個就和Direct3D中的D3DXMatrixPerspectiveLH()結果一樣。
最后,有必要說一下牽扯到寬高比的矩陣表示形式。
如圖6:
圖6
cot(a/2) = n / (h/2) = 2n/h
假設r = w/h,即寬高比
2n/w = 2n/(rh) = (1/r) * cot(a/2)
則有以下形式:
這個形式就和Direct3D中的D3DXMatrixPerspectiveFovLH()結果一樣。
注:
本文原本準備翻譯,后來沒耐心了,話說最近我做事一直都沒什么耐心了,所以沒怎么按照原文翻譯,而是把重要的列出來。
上面說的一些推導公式有些不用那么繁瑣,但是總的來看,很值得一讀。
本文版權歸作者 kanego 和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利.
總結
以上是生活随笔為你收集整理的投影矩阵的推导(转载)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 树莓派 docker homeassis
- 下一篇: 电脑与ps4共用音箱方案ps4怎么接电脑