本文由zhangbaochong原創,轉載請注明出處:http://www.cnblogs.com/zhangbaochong/p/5785100.html
之前為了方便觀察場景,我們采用的方法是鼠標控制旋轉視角和鏡頭拉伸,但是觀察點依然限制在一個球面內,目標點也始終為坐標原點。為了能夠自由的從各個角度、各個位置觀察場景,實現一個第一人稱攝像機是必不可少的。
1.攝像機視角矩陣推導
攝像機在空間有着特定的位置及朝向,它所觀察到的物體取決於物體與攝像機的相對位置。為了表示攝像機位置,我們可以使用一個3維向量;對於攝像機的的朝向,可以使用三個相互垂直的坐標軸的惟一地確定,即右、上、前三個方向。以攝像機為參考點,即以之為原點來觀察物體。因此,視角變換,本質上即把場景中所有的物體、連同攝像機,使用相同的變換,使得變換后攝像機位於場景的原點,且其三個坐標軸(右、上、前)與世界坐標系的X、Y、Z坐標軸重合。這時,變換后的所有物體的坐標值,即變為與攝像機的相對位置了。
我們把攝像機位置用[ px, py, pz ]來表示,其三個表示朝向的坐標軸分別表示為[ Rx, Ry, Rz ]、[ Ux, Uy, Uz ]、[ Lx, Ly, Lz ]。而視角變換的目的即把它的這些參數轉換為:位置[ 0, 0, 0 ],以及三個坐標軸:[ 1, 0,0 ]、[ 0, 1, 0 ]、 [ 0, 0, 1 ]。由於有平移和旋轉同時存在,因此該變換可以分為兩步進行:1. 平移到原點;2. 旋轉。
平移操作很簡單,即把[Px,Py, Pz,1]移回到原點,矩陣為:
旋轉操作需要點小技巧。我們的目的是把三個軸分別轉換成[1,0,0]、[0,1,0]、[0,0,1],令旋轉矩陣為M,可以表示為如下所示:
由上面式子可以看出,左邊的矩陣與M相乘后變為右邊的單位矩陣。這正是關鍵所在!我們知道,一個矩陣與它的逆矩陣相乘結果為單位矩陣,因此,要求矩陣M,我們可以求它的逆矩陣。求逆矩陣需要大量的計算,有沒有更好的方法?
答案是有的!有一點需要知道,正交矩陣的逆矩陣等於它的轉置矩陣。而正交矩陣的一個特點:正交矩陣的每行(每列)相互垂直。回來看下我們左邊的矩陣,每行分別對應了攝像機的三個相互垂直的軸,顯然它正是正交矩陣!好了,這下M的計算,就變成求它的轉置矩陣了。而轉置的計算再簡單不過了,直接給出:
現在,兩步需要的矩陣都有了,那最終的視角矩陣即兩次轉換的乘積,當然,由於平移變換的存在,現在要在4x4矩陣了,如下所示:
結果中P*R、P*U、P*L分別是位置向量與R、U、L三個向量的點積,通過查看左邊的矩陣相乘很容易看出來。
OK, 視角矩陣的推導完畢,現在我們知道:
任一時刻,通過攝像機位置P(Px,Py,Pz),以及三個坐標軸(R,U,L),可以得到當前的視角變換矩陣:
2.攝像機類的實現
攝像機參數主要有:位置、三個坐標軸、視角矩陣、投影矩陣、生成投影矩陣的四個參數(視角大小,寬高比,近裁剪平面,遠裁剪平面)。
實現的功能比較簡單,主要實現移動(前后左右移動),視角旋轉(左右搖頭,上下點頭)。
由於代碼比較簡單,故不再詳細說明了:
1 #pragma once 2 3 #include <Windows.h> 4 #include <DirectXMath.h> 5 #include <cmath> 6 class Camera 7 { 8 public: 9 Camera(); 10 11 //設置攝像機位置 12 void SetPosition(float x, float y, float z) { m_position = DirectX::XMFLOAT3(x, y, z); } 13 void SetPosition(DirectX::FXMVECTOR pos) { DirectX::XMStoreFloat3(&m_position, pos); } 14 15 //獲得攝像機位置方向等相關參數 16 DirectX::XMFLOAT3 GetPosition() const { return m_position; } 17 DirectX::XMFLOAT3 GetRight() { return m_right; } 18 DirectX::XMFLOAT3 GetLook() { return m_look; } 19 DirectX::XMFLOAT3 GetUp() { return m_up; } 20 21 DirectX::XMVECTOR GetPosotionXM() const { return DirectX::XMLoadFloat3(&m_position); } 22 DirectX::XMVECTOR GetRightXM() const { return DirectX::XMLoadFloat3(&m_right); } 23 DirectX::XMVECTOR GetLookXM() const { return DirectX::XMLoadFloat3(&m_look); } 24 DirectX::XMVECTOR GetUpXM() const { return DirectX::XMLoadFloat3(&m_up); } 25 26 //獲得投影相關參數 27 float GetNearZ() const { return m_nearZ; } 28 float GetFarZ() const { return m_farZ; } 29 float GetFovY() const { return m_fovY; } 30 float GetFovX() const { return atan(m_aspect * tan(m_fovY * 0.5f)) * 2.f; } 31 float GetAspect() const { return m_aspect; } 32 33 //獲得視圖投影矩陣 34 DirectX::XMMATRIX GetView() const { return DirectX::XMLoadFloat4x4(&m_view); } 35 DirectX::XMMATRIX GetProj() const { return DirectX::XMLoadFloat4x4(&m_proj); } 36 DirectX::XMMATRIX GetViewProj() const { return XMLoadFloat4x4(&m_view) * XMLoadFloat4x4(&m_proj); } 37 38 //設置投影矩陣 39 void SetLens(float fovY, float aspect, float nearz, float farz); 40 41 //設置視角矩陣 42 void LookAtXM(DirectX::FXMVECTOR pos, DirectX::FXMVECTOR lookAt, DirectX::FXMVECTOR worldUp); 43 void LookAt(const DirectX::XMFLOAT3& pos, const DirectX::XMFLOAT3& lookAt, 44 const DirectX::XMFLOAT3& worldUp); 45 46 //基本操作 47 void Walk(float dist); //前后行走 48 void Strafe(float dist); //左右平移 49 void Pitch(float angle); //上下點頭 50 void RotateY(float angle); //左右搖頭 51 52 //更新矩陣 53 void UpdateViewMatrix(); 54 private: 55 DirectX::XMFLOAT3 m_right; //位置和三個坐標軸 56 DirectX::XMFLOAT3 m_up; 57 DirectX::XMFLOAT3 m_look; 58 DirectX::XMFLOAT3 m_position; 59 60 float m_aspect; //投影相關參數 61 float m_fovY; 62 float m_nearZ; 63 float m_farZ; 64 65 DirectX::XMFLOAT4X4 m_view; //視角矩陣 66 DirectX::XMFLOAT4X4 m_proj; //投影矩陣 67 };
1 #include "Camera.h" 2 using namespace DirectX; 3 4 Camera::Camera(): 5 m_position(0.f,0.f,0.f), 6 m_look(0.f,0.f,1.f), 7 m_up(0.f,1.f,0.f), 8 m_right(1.f,0.f,0.f) 9 { 10 SetLens(0.25*XM_PI, 800.f / 600, 1.f, 1000.f); 11 } 12 13 //設置投影矩陣 14 void Camera::SetLens(float fovY, float aspect, float nearz, float farz) 15 { 16 m_fovY = fovY; 17 m_aspect = aspect; 18 m_nearZ = nearz; 19 m_farZ = farz; 20 XMMATRIX P = XMMatrixPerspectiveFovLH(fovY, aspect, nearz, farz); 21 XMStoreFloat4x4(&m_proj, P); 22 } 23 24 //設置視角矩陣 25 void Camera::LookAtXM(DirectX::FXMVECTOR pos, DirectX::FXMVECTOR lookAt, DirectX::FXMVECTOR worldUp) 26 { 27 XMVECTOR look = XMVector3Normalize(lookAt - pos); 28 XMVECTOR right = XMVector3Normalize(XMVector3Cross(worldUp, look)); 29 XMVECTOR up = XMVector3Cross(look, right); 30 31 XMStoreFloat3(&m_position, pos); 32 XMStoreFloat3(&m_look, look); 33 XMStoreFloat3(&m_right, right); 34 XMStoreFloat3(&m_up, up); 35 } 36 37 void Camera::LookAt(const DirectX::XMFLOAT3& pos, const DirectX::XMFLOAT3& lookAt, 38 const DirectX::XMFLOAT3& worldUp) 39 { 40 XMVECTOR p = XMLoadFloat3(&pos); 41 XMVECTOR l = XMLoadFloat3(&lookAt); 42 XMVECTOR u = XMLoadFloat3(&worldUp); 43 44 LookAtXM(p, l, u); 45 } 46 47 //前后行走 48 void Camera::Walk(float dist) 49 { 50 XMVECTOR pos = XMLoadFloat3(&m_position); 51 XMVECTOR look = XMLoadFloat3(&m_look); 52 pos += look * XMVectorReplicate(dist); //XMVectorReplicate(x)返回XMVector(x,x,x,x) 53 XMStoreFloat3(&m_position, pos); 54 } 55 56 //左右平移 57 void Camera::Strafe(float dist) 58 { 59 XMVECTOR pos = XMLoadFloat3(&m_position); 60 XMVECTOR right = XMLoadFloat3(&m_right); 61 pos += right * XMVectorReplicate(dist); 62 63 XMStoreFloat3(&m_position, pos); 64 } 65 66 //上下點頭 67 void Camera::Pitch(float angle) 68 { 69 XMMATRIX rotation = XMMatrixRotationAxis(XMLoadFloat3(&m_right), angle); 70 //向量矩陣相乘 71 XMStoreFloat3(&m_up, XMVector3TransformNormal(XMLoadFloat3(&m_up), rotation)); 72 XMStoreFloat3(&m_look, XMVector3TransformNormal(XMLoadFloat3(&m_look), rotation)); 73 } 74 75 //左右搖頭 76 void Camera::RotateY(float angle) 77 { 78 XMMATRIX rotation = XMMatrixRotationY(angle); 79 80 XMStoreFloat3(&m_right, XMVector3TransformNormal(XMLoadFloat3(&m_right), rotation)); 81 XMStoreFloat3(&m_up, XMVector3TransformNormal(XMLoadFloat3(&m_up), rotation)); 82 XMStoreFloat3(&m_look, XMVector3TransformNormal(XMLoadFloat3(&m_look), rotation)); 83 } 84 85 //更新視角矩陣 86 void Camera::UpdateViewMatrix() 87 { 88 XMVECTOR r = XMLoadFloat3(&m_right); 89 XMVECTOR u = XMLoadFloat3(&m_up); 90 XMVECTOR l = XMLoadFloat3(&m_look); 91 XMVECTOR p = XMLoadFloat3(&m_position); 92 93 r = XMVector3Normalize(XMVector3Cross(u, l)); 94 u = XMVector3Normalize(XMVector3Cross(l, r)); 95 l = XMVector3Normalize(l); 96 97 float x = -XMVectorGetX(XMVector3Dot(p, r)); 98 float y = -XMVectorGetX(XMVector3Dot(p, u)); 99 float z = -XMVectorGetX(XMVector3Dot(p, l)); 100 101 XMStoreFloat3(&m_right, r); 102 XMStoreFloat3(&m_up, u); 103 XMStoreFloat3(&m_look, l); 104 XMStoreFloat3(&m_position, p); 105 106 m_view(0, 0) = m_right.x; m_view(0, 1) = m_up.x; m_view(0, 2) = m_look.x; m_view(0, 3) = 0; 107 m_view(1, 0) = m_right.y; m_view(1, 1) = m_up.y; m_view(1, 2) = m_look.y; m_view(1, 3) = 0; 108 m_view(2, 0) = m_right.z; m_view(2, 1) = m_up.z; m_view(2, 2) = m_look.z; m_view(2, 3) = 0; 109 m_view(3, 0) = x; m_view(3, 1) = y; m_view(3, 2) = z; m_view(3, 3) = 1; 110 }
3.攝像機的使用
例子是上個demo,只不過是把控制視角的方式換成了剛實現的攝像機而已。
代碼下載:http://files.cnblogs.com/files/zhangbaochong/CameraDemo.zip