View Transform 詳解
什麼是View Transform
我們可以用照相機的原理來闡釋3D圖形的繪製過程,想象一下,我們在攝影的時候都需要做哪些工作,大致可分為如下幾個步驟
- 擺放好待拍攝的物品,或者人物。
- 調整好拍攝角度。
- 調整焦距。
- 拍攝。
好了,來分析一下,上面的第一步就相當於世界變換了,將一個模型置於一個公認的座標系中,這裡所謂的公認,也就是大家都遵守的,目的是保證待拍攝的物體和照相機在同一個座標系。第二步相當於檢視變換,這個過程是調整Camera到合適的位置以便拍攝,在3D程式中,也就是設定View Matrix了。第三步調整焦距,這就相當於3D程式設計中的投影變換。
View Transform的過程就是在世界座標系中擺放Camera的過程,並將頂點由世界座標系轉換到Camera Space,在Camera Space中,觀察者(Camera)位於座標原點,觀察方向指向Z軸正方向。
為什麼要進行View Transform?
在world space中,camera並不一定位於座標原點,並且觀察方向不一定指向Z軸正方向,對於投影變換及其他的一些操作來說,如果不滿足這兩個條件,後續的的操作就會變得非常低效,所以為了提高效率,我們需要進行view transform。
View Transform的作用
View Transform主要有下面兩個作用。
- 移動camera,使其位於world space的座標原點
- 旋轉camera,使其朝向z軸正方向,也就是視線由原點指向z軸正方向。
這兩個過程,前一個實際上是平移,後一個實際上是旋轉。你可以想象成Camera也有三個座標軸x,y,z,檢視變換的過程就是將Camera的座標軸與世界座標系的座標軸對齊的過程。
注意:view transform中,所有位於world space中的models都隨著camera一起變換,所以視野並未發生變化,具體過程見下圖
如何求解View Matrix?
使用D3D函式
使用下面的D3D函式可以計算view matrix,第一個引數是輸出引數,返回求得的檢視矩陣,第二個引數是眼睛的位置,第三個引數是觀察點中心,最後一個引數是向上向量。該函式的最後兩個字母表示左手系(Left Hand)。
D3DXMatrixLookAtLH(&M, &eyePt, &lookCenter, &upVec) ;
手動求解
手動求解View matrix並不是難事,一個camera一般有如下四個屬性,
- 向前向量(direction),相當於Z軸
- 向上向量(up vector),相當於Y軸
- 向右向量(right vector),相當於X軸
- 位置(position)
其中前三個向量要求是相互垂直的。假設我們分別用d, u, v和p來表示這四個變數。並假設待求的檢視矩陣為V,根據前面的介紹我們知道,V的作用就是將攝像機移動到原點,並將攝像機的三個向量分別與座標軸對齊,d與z軸正方向對齊,u與y軸正方向對齊,r與x軸正方向對齊。假設將攝像機與座標軸對齊的矩陣為V,那麼V的推導過程如下。
通常在實際的程式設計中,只會給出如下三個量。至於d和r這兩個量,只能通過計算求得。
- 攝像機(眼睛)的位置(eye point),相當於p
- 觀察點中心(look at),假設為向量c
- 向上向量(up vector),相當於u
可以通過下面方法求得d, r和u。
- d = c - p,下面第一幅圖
- r = d x u,下面第二幅圖
- u = r x d,下面第三幅圖
注意上面的x是叉積運算(cross product),現在p,d,r,u四個量都已經知道,於是就可以根據上面的矩陣M求得檢視變換矩陣了,對應的程式碼如下。該函式有三個引數,分別是攝像機位置p,向上向量u和視點中心lookAt。
D3DXMATRIX buildViewMatrix(D3DXVECTOR3& p, D3DXVECTOR3& u, D3DXVECTOR3& lookAt) { // Calculate d D3DXVECTOR3 d = lookAt - p; D3DXVec3Normalize(&d, &d); // Calculate r D3DXVECTOR3 r; D3DXVec3Cross(&r, &u, &d); D3DXVec3Normalize(&r, &r); // Calculate up D3DXVec3Cross(&u, &r, &d); D3DXVec3Normalize(&u, &u); // Fill in the view matrix entries. float x = -D3DXVec3Dot(&p, &r); float y = -D3DXVec3Dot(&p, &u); float z = -D3DXVec3Dot(&p, &d); D3DXMATRIX M; M(0,0) = r.x; M(1,0) = r.y; M(2,0) = r.z; M(3,0) = x; M(0,1) = u.x; M(1,1) = u.y; M(2,1) = u.z; M(3,1) = y; M(0,2) = d.x; M(1,2) = d.y; M(2,2) = d.z; M(3,2) = z; M(0,3) = 0.0f; M(1,3) = 0.0f; M(2,3) = 0.0f; M(3,3) = 1.0f; return M; }
注意事項,在求取View Matrix的時候有幾點是需要注意的:
- 叉積滿足右手法則。
- 叉積不滿足交換律。 a x b = - b x a
- DirectX使用左手系,滿足左手法則。
只要最終求得的三個向量,up, right和d滿足左手法則即可。如果應用了View Matrix之後發現模型左右或者上下顛倒了,那麼就說明求取的時候沒有滿足左手系。
好了,矩陣求解完畢,趕快使用SetTransform(D3DTS_VIEW, &M) ;來試試吧,效果和使用函式D3DXMatrixLookAtLH是一樣的!
Happy Coding!!!