OpenGL取景變換(視圖變換)矩陣推導
標籤(空格分隔): OpenGL VR 遊戲開發
前言
關於取景變換(視圖變換)矩陣的推導本人查過許多資料, 包過關於openGL的和數學方面, 數學方面的資料很嚴謹, 推導過程環環相扣, 但是數書知識畢竟是理論, 怎麼將理論變成實現的代碼, 數學知識是不涉及的. 代碼原理方面的知識只能查找openGL或圖形學相關的資料, 但是這些資料有個缺點就是涉及數學原理的地方不會寫的很清楚, 很多東西都是一筆帶過, 沒有那種環環相扣的嚴謹性, 所以到最後就發現查了很多資料都發現推導過程是脫節的, 代碼不是從數學知識平滑過渡過來的.
本文旨在結合數學知識一步一步的推導出取景變換的變換矩陣, 從最基本的向量相乘到最終的代碼實現.
使用線性變換推導基本座標變換公式
先從簡單的情況入手, 讓兩個座標系的原點相同, 基向量不同, 設世界座標系
即:
令:
則有
稱之爲座標轉換公式.
設世界座標系中
由:
得出
由於
從而得出
引入齊次座標推導最終矩陣
上面只涉及了線性變換部分, 由於平移不能用線性變換來表示, 所以要引入仿射變換, 首先要做的是將上述座標轉換爲齊次座標, 同時將變換矩陣改升級爲4x4的齊次矩陣,
而原來的變換矩陣就變成了:
有這麼一條理論: 在發生座標系轉換時, 如果已知一個點在老座標系的座標, 需要求這個點在新座標系的座標, 只需要對這個點做 新座標系發生過的變換 的逆過程
這個逆過程就是將觀察座標系變換回世界座標系的過程, 這個過程相對應的矩陣就是取景變換矩陣. 對於任意觀察座標系, 都可以通過先平移再旋轉, 從而變換回世界座標系.
設世界座標系中的一點
原點重合後, 再應用上面推導過的線性變換就可以與世界座標系完全重合了, 於是左乘之前的線性變換矩陣
結果是:
即:
OpenGL取景變換矩陣源代碼解析
首先簡單介紹一下觀察座標系三個基向量的計算, 在代碼中一般會傳入三個信息:
- 觀察點: 就是相機的位置.
- 觀察中心: 就是觀測的中心.
- up向量: 相機向上的大致方向, 是個向量.
利用觀察點和觀察中心就可以算出觀察平面的法向量n(與觀測方向相反), 法向量與up向量叉乘就可以得出第三個向量u.
(由於傳入的up向量不一定是嚴格的向上, 可能是斜的, 所以還要用n叉乘u得出矯正後的up向量v)
以下是iOS平臺在xcode中看到的OpenGL求取景變換矩陣的代碼:
GLK_INLINE GLKMatrix4 GLKMatrix4MakeLookAt(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ)
{
GLKVector3 ev = { eyeX, eyeY, eyeZ }; // 觀察點
GLKVector3 cv = { centerX, centerY, centerZ }; // 觀測中心
GLKVector3 uv = { upX, upY, upZ }; // up向量, 可以與觀測方向不垂直, 但是不能平行
// 被觀察平面的法向量, 與觀測方向相反, n對應於OpenGL的z軸
GLKVector3 n = GLKVector3Normalize(GLKVector3Add(ev, GLKVector3Negate(cv)));
// 叉乘得到第一根軸, u對應於OpenGL的x軸
GLKVector3 u = GLKVector3Normalize(GLKVector3CrossProduct(uv, n));
// 再次叉乘得到矯正後的up向量, v對應於OpenGL的y軸
GLKVector3 v = GLKVector3CrossProduct(n, u);
// 採用了列主序儲存(用一個一維數組依次儲存了第一列, 第二列, ...),
GLKMatrix4 m = { u.v[0], v.v[0], n.v[0], 0.0f,
u.v[1], v.v[1], n.v[1], 0.0f,
u.v[2], v.v[2], n.v[2], 0.0f,
GLKVector3DotProduct(GLKVector3Negate(u), ev),
GLKVector3DotProduct(GLKVector3Negate(v), ev),
GLKVector3DotProduct(GLKVector3Negate(n), ev),
1.0f };
return m;
}
代碼註釋應該已經很清晰了, 這裏再額外解釋幾個點:
1. GLKVector3Normalize: 功能是把向量歸一化, 就是轉換成單位向量.
2. GLKVector3DotProduct: 求向量的點乘.
3. GLKVector3Negate: 將向量轉成負的.
4. 外部傳入的up向量(upX, upY, upZ)不一定與觀測方向垂直, 所以要通過叉乘算出.
結語
雖然這裏只證明了取景變換的推導過程, 但是這個原理可以推廣到OpenGL中所有的座標變換中去.
在OpenGL裏, 任何變換都可以用一個齊次矩陣表示, 因爲所以的變換都可以分解成多個基本變換(旋轉, 縮放, 平移), 每個基本變換都能用一個齊次矩陣表示, 這些矩陣相乘就得到了最終的變換矩陣.
附錄:
(1) 正交矩陣: 正交矩陣的定義就是
(2) 逆向變換理論: 其實這條理論在高中學二維座標系轉換就學過了, 只是時間久了就忘了, 但是很容易理解. 空間中有一個座標系
這樣說可能很繞, 其實可以簡單點理解, 想象座標轉換隻是複製了一個立方體, 然後旋轉平移到另一個地方, 原來立方體裏的所有點都在新立方體裏有一個替身, 欲求老立方體中的一個點在新立法體座標, 只要將它的替身做相逆的變換, 他倆就重合了, 這時替身的座標就是欲求的.