ViewMatrix用於直接將World座標系下的座標轉換到Camera座標系下。已知相機的座標系。還有相機在世界空間下的座標.就可以求出ViewMatrix。如下進行具體推導。html
令UVN爲相機座標系下的三個基,,對於一個相機來講,它在開始的時候和世界座標系是重合的,用戶控制相機在世界空間中移動以後,相機的狀態可以用兩個屬性來描寫敘述——朝向和位置。也就是說。有了這兩個屬性,一個相機模型在世界中的狀態就肯定了。算法
而這兩個屬性,咱們用變換的理論來描寫敘述,就是旋轉和平移。函數
可以想象,對於世界中的不論什麼一個相機狀態,咱們都可以把它當作是:相機先環繞自身基原點旋轉必定的角度,而後平移到世界空間的某個地方。下圖展現了這個過程post
圖一 . 相機的變換與逆變換
spa
圖中,紅色是相機的基,而黑色是世界的基,也就是參考系。.net
小人是世界中的一個物體。相機在移動以前,兩個基是重合的。當相機在屏幕中定位時。它首先會進行朝向的肯定——旋轉。而後進行位置的肯定——平移。圖中的Rotation和Translation兩步就是相機定位時所發生的變換。可以看到相機相對於小人的運動。而當進行相機變換的時候,小人應該從世界基變換到相機的基裏面。3d
這樣,他應該進行一個相機定位的逆定位。先逆平移小人和相機。而後再逆旋轉小人和相機。最後相機歸位,小人隨相機變到了相機空間。這是由Inverse Translation和Inverse Rotation兩個步驟完畢的。這兩個步驟就是相機變換。code
現在咱們推導這個變換。咱們把關係寫出來。相機自己的變換C包含兩個元素
orm
C = TRhtm
當中T是平移變換,R是旋轉變換。而相機變換是相機自己變換的逆變換
這個C^-1就是咱們要求出的相機變換。
當中T^-1很是easy求出,即
R^(-1)比較難求出,這裏用到了正交基一些知識,可以參考下碰撞檢測之Ray-Cylinder檢測前面關於正交基的部分。
當相機變換進行完Inverse Translation這一步以後,相機的原點和世界原點就重合了,也就是處理完了關於平移的變換。接下來咱們要作的是逆旋轉,而事實上逆旋轉的目的,就是要獲得眼下世界座標中通過逆平移的小人在相機座標系中的座標。
由座標轉換公式
對於世界座標中的已經通過逆平移的座標v’,它在相機座標系R中的座標是v’’,而相機座標系就是
則相機變換的完整公式就是
當中v是小人在世界空間中的座標。v’’是小人在相機空間中的座標。則相機變換矩陣就是
常見的求ViewMatrix的狀況有三種。一種是用LookAt函數。另一種是類似FPS遊戲中經過pitch和yaw來算,另外一種是類似軌跡球的算法。
最後的方法都是轉化爲求出相機的座標系的基。
這裏參考是左手座標系,而且在相機空間,相機的forward是Z軸的負方向。和Unity還有OpenGL一致。
圖二 . 相機的左手座標系
例如如下圖,這樣的求法需要知道攝像機的位置 eye。一個up向量(global),還有攝像機的觀察點at
圖三 . LookAt函數模型
Matrix4x4 LookAt(const Vector3& eye, const Vector3& target, const Vector3& up) { Vector3 z((eye - target).normalized()); Vector3 x((Vector3::Cross(z, up).normalized())); Vector3 y(Vector3::Cross(x, z)); Matrix4x4 result; result[0] = x.x; result[4] = x.y; result[8] = x.z; result[12] = -Vector3::Dot(x, eye); result[1] = y.x; result[5] = y.y; result[9] = y.z; result[13] = -Vector3::Dot(y, eye); result[2] = z.x; result[6] = z.y; result[10] = z.z; result[14] = -Vector3::Dot(z, eye); result[3] = result[7] = result[11] = 0.0f; result[15] = 1.0f; return result; }
//C++ qDebug() << Transform::LookAt(Vector3(1, 2, 3), Vector3(0, 10, 0), Vector3::up);
圖四 . LookAt函數計算結果
//Unity transform.position = new Vector3(1, 2, 3); transform.LookAt(new Vector3(0, 10, 0), Vector3.up); Debug.Log(Camera.main.worldToCameraMatrix);
圖五 . Unity中的計算結果
(貌似Unity中的Transform.LookAt函數是直接改動的Camera的Rotation?)
經過以前的結論
若是原始的座標系爲x-(1,0,0), y-(0,1,0), z(0,0,1),這個座標系通過一個旋轉矩陣Matrix旋轉以後,新的座標系就是這個旋轉矩陣的轉置三個列向量。
事實上這裏要求的僅僅是旋轉矩陣的轉置。
注意相機的Z方向並不是相機正對的方向,而是相機的背面。
Matrix4x4 Transform::FPSView(const Vector3& eye, Quaternion rotation) { Matrix4x4 rotMatrix = rotation.GetRotMatrix().transpose(); Vector3 x(rotMatrix[0], rotMatrix[4], rotMatrix[8]); Vector3 y(rotMatrix[1], rotMatrix[5], rotMatrix[9]); Vector3 z(-rotMatrix[2], -rotMatrix[6], -rotMatrix[10]); Matrix4x4 result; result[0] = x.x; result[4] = x.y; result[8] = x.z; result[12] = -Vector3::Dot(x, eye); result[1] = y.x; result[5] = y.y; result[9] = y.z; result[13] = -Vector3::Dot(y, eye); result[2] = z.x; result[6] = z.y; result[10] = z.z; result[14] = -Vector3::Dot(z, eye); result[3] = result[7] = result[11] = 0.0f; result[15] = 1.0f; return result; }
算例
//C++ qDebug() << Transform::FPSView(Vector3(1,2,3), Quaternion::Euler(30, 45, 60));
圖六 . FPV函數計算結果
//unity transform.position = new Vector3(1, 2, 3); transform.rotation = Quaternion.Euler(30, 45, 60); Debug.Log(Camera.main.worldToCameraMatrix);
圖七 . Unity中執行結果
Understanding the View Matrix - http://www.3dgep.com/understanding-the-view-matrix/
Tutorial 3 : Matrices - http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
OpenGL Transformation - http://www.songho.ca/opengl/gl_transform.html
推導相機變換矩陣 - http://blog.csdn.net/popy007/article/details/5120158