原文地址: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中初始化的。现在我们可以从摄像机类中获取。
代码:
展示效果: