OGL(教程14)——攝像機控制1
原文地址:http://ogldev.atspace.co.uk/www/tutorial14/tutorial14.html
背景知識:
在之前的章節中,我們學習如何把攝像機放在3D世界中的任意位置。下一步就是允許使用者能夠控制它。移動將不會被限制,使用者要能夠向任意的方向移動。控制攝像機有兩個裝置——鍵盤和滑鼠,鍵盤用於控制方向,而滑鼠用於調整朝向。這個和第一視角的遊戲很像。本節主要集中在鍵盤控制,下一節會將滑鼠控制。
我們現在要支援四個方向鍵控制方式。記得攝像機的變換定義了位置、朝向、還有向上的向量,當我們使用鍵盤的時候,只是改變了它的位置,我們不能改變攝像機的朝向和向上的向量。
為了控制鍵盤,我們需要使用GLUT的api函式:glutSpecialFunc()。這個函式註冊了一個回撥哈數,每次觸發的時候,都會呼叫指定的key的操作。這些方向鍵有:PAGE-UP/PAGE-DOWN/HOME/END/INSERT,如果你想要跟蹤某個鍵,需要使用glutKeyboardFunc()。
程式碼註釋:
相機的功能封裝在Camera類中。這個類儲存了攝像機的屬性,當我們移動的時候可以改變他的屬性。這些屬性從管線類中的變換矩陣得到。
(Camera.h)
class Camera { public: Camera(); Camera(const Vector3f& Pos, const Vector3f& Target, const Vector3f& Up); bool OnKeyboard(int Key); const Vector3f& GetPos() const const Vector3f& GetTarget() const const Vector3f& GetUp() const private: Vector3f m_pos; Vector3f m_target; Vector3f m_up; };
這個是Camera類的宣告。它儲存了攝像機的屬性:位置、朝向、向上向量。兩個構造器可以被使用。預設的建構函式簡單的把攝像機放在原點,朝向是z軸方向,向上向量為(0,1,0)。另外一個建構函式可以傳入任意的指定的向量。OnKeyboard()函式提供監聽鍵盤處理事件。
(Camera.cpp:42)
bool Camera::OnKeyboard(int Key) { bool Ret = false; switch (Key) { case GLUT_KEY_UP: { m_pos += (m_target * StepSize); Ret = true; } break; case GLUT_KEY_DOWN: { m_pos -= (m_target * StepSize); Ret = true; } break; case GLUT_KEY_LEFT: { Vector3f Left = m_target.Cross(m_up); Left.Normalize(); Left *= StepSize; m_pos += Left; Ret = true; } break; case GLUT_KEY_RIGHT: { Vector3f Right = m_up.Cross(m_target); Right.Normalize(); Right *= StepSize; m_pos += Right; Ret = true; } break; } return Ret; }
這個函式根據鍵盤事件作出處理。GLUT定義了巨集,用於控制方向。不幸的是,這個巨集只是int而不是列舉。
向前和向後很簡單。因為移動總是沿著朝向移動。我們只需要加減朝向向量即可。朝向向量本身不變。注意到在加減向量之前,我們加了一個常量,這裡成為StepSize。我們在所有的方向上移動都加了這個步長。StepSize提供了移動的速度。為了保持StepSize保持連續,我們要確保它總是乘以單位向量。
兩側移動要複雜一點。它的移動被定義為沿著朝向和向上向量構成平面垂直的那個向量移動。這個平面把3D空間氛圍兩個部分。有兩個向量和這個平面垂直。我們可以叫做左和右。它是通過朝向和向上向量叉乘得到。注意叉乘和順序有關。當得到左右向量之後,我們需要單位化它,然後用StepSize縮放它,然後加上位置。同樣,朝向和向上向量不會受到影響。
注意到上面的函式利用到了+=和-=。
(tutorial14.cpp:73)
static void SpecialKeyboardCB(int Key, int x, int y)
{
GameCamera.OnKeyboard(Key);
}
static void InitializeGlutCallbacks()
{
glutDisplayFunc(RenderSceneCB);
glutIdleFunc(RenderSceneCB);
glutSpecialFunc(SpecialKeyboardCB);
}
這裡我麼定義處理鍵盤事件的函式。回撥函式接收鍵盤事件。
(tutorial14.cpp:55)
p.SetCamera(GameCamera.GetPos(), GameCamera.GetTarget(), GameCamera.GetUp());
之前我們初始化了攝像機的引數,在Pipeline中初始化的。現在我們可以從攝像機類中獲取。
程式碼:
展示效果: