首先解决的一个问题:什么是镜头?
我们可以想象拍戏那种镜头,对着什么东西,就能拍出什么东西,然后我们就可以在电视屏幕或者电影屏幕上看到这些东西。3D世界里面也是这样定义一个镜头,在这个镜头范围之内的东西就可以显示在电脑屏幕中。
如下图在一个方各地上行走:
下面是把镜头抽象起来:
拍戏的时候,这个镜头是个实体,那么在我们的3D世界里面我们也是这样把镜头当做一个实体,在代码中体现出来就是一个类。
在游戏中控制镜头前进,后退,左移,右移,其实就是在代码中操作这个类。
所以问题就归结为如何构建这个类,和如何操作这个类了。
首先构建这个类的对象成员有如下数据:
class Camera
{
protected:
D3DXMATRIX mView;//操作镜头的主要矩阵
D3DXMATRIX mProj;//投影到屏幕上的矩阵
D3DXMATRIX mViewProj;//从镜头空间到投影到屏幕上的矩阵
D3DXVECTOR3 mPosW;//镜头位置
D3DXVECTOR3 mRightW;//镜头的由向量
D3DXVECTOR3 mUpW;//镜头的上向量
D3DXVECTOR3 mLookW;//镜头的前向量
float mSpeed;//镜头的速度
镜头的坐标如下:
右上角的代表镜头,指向前的叫前向量,指向上的叫上向量,指向右的叫右向量。这样就构成了镜头坐标空间。就是构建了这样的空间,才可以让镜头前面的物体相对于镜头的位置得以确定!是在看不懂的朋友先熟记一下这个概念,然后建议补一补几何学。
mProj和mViewProj矩阵基本上在所有3D世界都要用到的。这是图形学的构造原理,有空再专门解析一下吧。简单解析一下就是说:利用这些矩阵才能把定位在世界坐标空间的物体投影到屏幕上。
跟本章相关的就是mView矩阵了,就是利用了这个矩阵才能操作镜头的。这个矩阵的计算方法如下:
定义在类中:
class Camera
{
public:
Camera();
protected:
void buildView();
实现函数:
void Camera::buildView()
{
// 这里是前向量单位化.
D3DXVec3Normalize(&mLookW, &mLookW);
D3DXVec3Cross(&mUpW, &mLookW, &mRightW);//作叉乘可以确保这2个向量是正交的,也就是都互相垂直。
D3DXVec3Normalize(&mUpW, &mUpW);
D3DXVec3Cross(&mRightW, &mUpW, &mLookW);//再次叉乘保证正交
D3DXVec3Normalize(&mRightW, &mRightW);
//这里是根据几何公式来填写这个需要的变换矩阵的,至于是什么公式,我也不记得了,因为公式很复杂,死背不是好办法,但是原理要知道,就是利用这样的矩阵来转换镜头。
float x = -D3DXVec3Dot(&mPosW, &mRightW);
float y = -D3DXVec3Dot(&mPosW, &mUpW);
float z = -D3DXVec3Dot(&mPosW, &mLookW);
mView(0,0) = mRightW.x;
mView(1,0) = mRightW.y;
mView(2,0) = mRightW.z;
mView(3,0) = x;
mView(0,1) = mUpW.x;
mView(1,1) = mUpW.y;
mView(2,1) = mUpW.z;
mView(3,1) = y;
mView(0,2) = mLookW.x;
mView(1,2) = mLookW.y;
mView(2,2) = mLookW.z;
mView(3,2) = z;
mView(0,3) = 0.0f;
mView(1,3) = 0.0f;
mView(2,3) = 0.0f;
mView(3,3) = 1.0f;
}
下面就是以鼠标和键盘为参数不断更新上面这些参数,得到不断移动镜头的感觉:
void Camera::update(float dt)
{
// 这里是控制方法wsda为前后左右。
D3DXVECTOR3 dir(0.0f, 0.0f, 0.0f);
if( gDInput->keyDown(DIK_W) )
dir += mLookW;
if( gDInput->keyDown(DIK_S) )
dir -= mLookW;
if( gDInput->keyDown(DIK_D) )
dir += mRightW;
if( gDInput->keyDown(DIK_A) )
dir -= mRightW;
// Move at mSpeed along net direction.
D3DXVec3Normalize(&dir, &dir);
mPosW += dir*mSpeed*dt;//这里是控制其速度,可以改变mSpeed的大小以改变其速度,记得mSpeed为类的私有成员。
mPosW.y = 2.0f;//这里是限制其高度,相当于人的高度。如果把这里去掉的话,就可以飞起来了。
if(mPosW.x >= 49.0f)//这下面的代码是控制不要超出了方格地的范围了。
mPosW.x = 49.0f;
if(mPosW.x <=-49.0f)
mPosW.x = -49.0f;
if(mPosW.z >= 49.0f)
mPosW.z = 49.0f;
if(mPosW.z <= -49.0f)
mPosW.z = -49.0f;
// 下面是鼠标控制代码,控制其向左和向右,向上和向下看.
float pitch = gDInput->mouseDY() / 150.0f;
float yAngle = gDInput->mouseDX() / 150.0f;
// 向上下方向看时的变换算法.
D3DXMATRIX R;
D3DXMatrixRotationAxis(&R, &mRightW, pitch);
D3DXVec3TransformCoord(&mLookW, &mLookW, &R);
D3DXVec3TransformCoord(&mUpW, &mUpW, &R);
// 向左向右看时的算法.
D3DXMatrixRotationY(&R, yAngle);
D3DXVec3TransformCoord(&mRightW, &mRightW, &R);
D3DXVec3TransformCoord(&mUpW, &mUpW, &R);
D3DXVec3TransformCoord(&mLookW, &mLookW, &R);
// 然后需要重新调用前面的函数根据上面的新参数更新计算,这样达到移动镜头的目的.
buildView();
mViewProj = mView * mProj;
}
下面是调用函数,我写了个namespace包起来,方便修改:
#include "CameraRun.h"
#include "Camera.h"
namespace CameraRun
{
Camera camera;
void initData()
{
gCamera = &camera;
gCamera->pos() = D3DXVECTOR3(-20.0f, 2.0f, 0.0f);
}
void updateAll(float dt)
{
gCamera->update(dt);
}
}
玩这个程序只需要修改上面说明的地方就可以自己测试了。
可以修改的地方:
1 修改成为可以飞起来的
2 修改其速度
3 改成不受方格地限制
4 更难的就是修改限制其俯视的角度
这里是镜头类下载:
http://download.csdn.net/detail/kenden23/6491871
这里是框架下载:
http://download.csdn.net/detail/kenden23/6491875
都是源代码。