標籤:d3d opengl 跨平台
遊藍海(http://blog.csdn.net/you_lan_hai)
DirectX 3D與OpenGL座標系統的差異性,給我們帶來很大的麻煩,讓跨平台編程的新手很困惑。最近在做一個跨平台的遊戲,仔細看了下兩者的矩陣,發現並沒有什麼大區別,將d3d左手系的矩陣傳遞給opengl shader完全可以正常工作。
先說一下兩者一些概念上的區別:
(1)座標系統不同
d3d左手座標系,opengl右手座標系
(2)矩陣行序不同
d3d行優先,opengl列優先。這兩個不同,直接導致了座標變換順序與矩陣乘法順序的相反性。如果是先縮放,再旋轉,最後平移,對應的矩陣分別為S、R、T,則d3d的最終矩陣為M = S * R * T,opengl為M = T * R * S
(3)裁減空間z取值範圍不同
d3d是[0, 1],opengl是[-1, 1]
表面上來看,兩者矩陣差別很大,但其實不然。
1.左右手座標系
對於顯卡裝置來說,裝置座標系是左手座標系,即z軸指向螢幕裡面,z值越大表示距離視線越遠。因此,opengl的右手系,在進入裁減空間的時候,會轉換成左手系。這也就是說,在渲染管線內部,座標系是統一的。不管是左手座標系矩陣,還是右手座標系矩陣,只要變換到裁減空間中的點是左手系就可以了。
2.矩陣行序
行矩陣和列矩陣,在邏輯上一個是另一個的轉置,但在物理存貯結構上卻是完全一致的。如一個平移變換(x, y, z):
需要注意的是,矩陣乘法並不關心矩陣是行矩陣還是列矩陣,都是按照第一個矩陣的行去乘以第二個矩陣的列。對於列矩陣而言,這正是其蹩腳的地方,為了保證乘法意義的有效性,其座標變換順序跟矩陣乘法順序恰好相反。
還要注意一點,在shader中,opengl的矩陣乘法規則跟d3d是不同的。按照矩陣乘法規則(第一個矩陣行*第二個矩陣列):
d3d矩陣乘法: Ma(0 1 2 3) * Mb(0 4 8 12)
opengl矩陣乘法: Ma(0 4 8 12) * Mb(0 1 2 3)
因此,對於opengl shader而言,變換順序跟矩陣乘法順序依然是反的。如果我們能將傳入opengl shader的矩陣做一次轉置,那麼opengl shader的矩陣乘法意義將跟d3d shader完全一致!
3.修改投影矩陣
由於opengl的裁減空間z取值範圍為[-1, 1]跟d3d的[0, 1]不同,我們不能簡單的使用d3d投影矩陣,必須重新定義d3d投影矩陣。
void Matrix::perspectiveProjectionLH2( float fov, float aspectRatio, float nearPlane, float farPlane ){ float h = (1.0f / tanf(fov * 0.5f)); float w = h / aspectRatio; float a = (farPlane + nearPlane) / (farPlane - nearPlane); float b = -2.0f * farPlane * nearPlane / (farPlane - nearPlane); m[0][0] = w; m[0][1] = 0; m[0][2] = 0; m[0][3] = 0; m[1][0] = 0; m[1][1] = h; m[1][2] = 0; m[1][3] = 0; m[2][0] = 0; m[2][1] = 0; m[2][2] = a; m[2][3] = 1; m[3][0] = 0; m[3][1] = 0; m[3][2] = b; m[3][3] = 0;}
4.總結
使用左手系變換來統一兩個平台是比較方便的。總結一下修改opengl渲染管線的步驟:
(1)在c++層統一使用左手座標系變換;
(2)修改投影矩陣,以適應裁減空間z座標範圍[-1, 1];
(3)矩陣在傳入shader的時候,將矩陣的轉置矩陣傳入;
(4)在shader層,統一使用左手座標系變換。
如果,不想修改shader中的變換,只用做到(1)和(2)就夠了。
5.更多閱讀
推導投影矩陣 http://blog.csdn.net/popy007/article/details/4091967
跨越opengl和d3d的鴻溝 http://www.cppblog.com/topjackhjj/articles/157038.html
統一D3D與OpenGL座標系統