本篇文章中,我们以核心思想为突破口,从原理介绍到一个C++类的写法,一步一步带领大家实现了一个第一人称三维摄像机的C++类。然后我们在这个摄像机类的帮助下,放出了一个几乎贯穿了我们之前学到的所有DirectX相关知识的“三维场景漫游”示例程序,算是对我们之前学的固定功能流水线这套渲染体系的总结。这个“三维场景漫游”示例程序的代码量有一千行,包括了Direct3D初始化,DirectInput输入处理,顶点缓存,光照与材质,文字输出,颜色,纹理贴图,四大变换,网格模型,X文件载入等等知识(当然还有默认被开启的深度缓存)。下面我们先放出一张运行截图:
好吧,正文开始,来卡看如何一步一步实现一个第一人称三维摄像机。
一、一些概述
回想我们之前文章的配套示例程序中,我们都是通过封装的DirectInput类来处理键盘和鼠标的输入,对应地改变我们人物模型的世界矩阵来达到移动物体,改变观察点的效果。其实我们的观察方向乃至观察点都是没有变的,变的只是我们3D人物的位置。举个例子吧,我们之前的示例程序就像是在小区里面安放着的摄像头,位置和观察点都是固定的,变的只是来来往往的人群。贴几张我们之前文章配套的示例程序截图来让大家体会一下:
之前的示例程序,和三维观察相关的四大矩阵变换我们都封装在了一个叫Matrix_Set()的函数中,而Matrix_Set()通常只用在初始化资源的时候调用一次而已。其中,四大变换中的取景变换,投影变换和视口变换都是一直老老实实地在Matrix_Set()中尽职尽责地呆着,当然,除了世界矩阵变换这个调皮的野孩子之外。世界矩阵由于实时处理的需要,自从第一次介绍完四大变换的时候让他在Matrix_Set()中呆了一次,在此之后的示例程序中我们都是把世界矩阵的设置放在Direct3D_Update()以及Direct3D_Render()之中的。因为我们必须实时地处理输入,让人物模型的世界矩阵能根据我们的输入进行相应的调整,这样才能做出人物模型随着键盘和鼠标的输入,随心所欲地受我们控制的效果出来。
但其实我们之前实现的那种所谓的“视角改变”还是来得不彻底不爽快,说白了就是用D3DXMatrixLookAtLH在资源初始化时固定住视角,在程序运行过程中接收到消息并改变三维人物模型的世界矩阵而已。我们这篇文章的主要讲解点就是手把手地教大家创建出一个可以在三维空间中自由移动的摄像机类,这样利用这个摄像机类写出来的示例程序就像是我们身临其境地在三维空间中真正地自由翱翔一样。浅墨怎么在写上面这段文字的时候,脑海中闪现的竟然是凤凰传奇的那句“在你的心上,自由地飞翔”,黑了- -
我们准备给这个摄像机类取名为CameraClass,这个类的功能非常适合用于三维飞行模拟类游戏(比如王牌空战)以及第一人称视角游戏(比如反恐精英(CS),穿越火线(CF))中。
二、开始设计摄像机类
首先,给大家介绍一下这个摄像机类的核心思想,那就是用四个分量:右分量(rightvector)、上分量(up vector)、观察分量(lookvector)和位置分量(position vector),来确定一个摄像机相对于世界坐标系的位置和朝向。并根据这四个分量计算出一个取景变换矩阵,完全取代之前的示例程序用D3DXMatrixLookAtLH创建的取景变换矩阵。
在世界坐标系中,这几个分量都是通过向量表示的,并且实际上他们为摄像机定义了一个局部坐标系。
其中,摄像机的左分量、上分量和观察分量定义了摄像机在世界坐标系中的朝向,因此他们也被称为方向向量。在通常的情况下,方向向量都是单位向量(模为1),并且两两之间相互垂直,也就是我们常说的标准正交。
其实,这三个向量我们完全可以理解为三维坐标系中的X,Y,Z轴。
另外,我们需要了解标准正交矩阵的一个重要性质,那就是标准正交矩阵的逆矩阵与其转置矩阵相等。
用上面提到的右分量(right vector)、上分量(up vector)、观察分量(look vector)和位置分量(position vector)这四个向量来描述摄像机的话,其中的位置分量其实我们可以把他看做一个描述位置的点,那么有用的就还3个分量,每个分量我们可以进行沿着其平移和绕着其旋转两种操作,那么我们可以想到的方式就是2x 3=6种,就是以下这六种运动方式:
● 沿着右分量平移
● 沿着上分量平移
● 沿着观察分量平移
● 绕着右分量旋转
● 绕着上分量旋转
● 绕着观察分量旋转
另外,我们新创建的这个摄像机类在我们的示例程序中完全“上位”了,取代了之前我们在Matrix_Set()中实现的取景变换和投影变换,所以在我们新的程序中,Matrix_Set()函数将不复存在,单单用一个摄像机类就行了。
根据上面的讲解,我们可以勾勒出这个CameraClass类的轮廓如下:
-
-
-
-
-
-
-
- #pragma once
-
- #include <d3d9.h>
- #include <d3dx9.h>
-
- class CameraClass
- {
- private:
-
- D3DXVECTOR3 m_vRightVector;
- D3DXVECTOR3 m_vUpVector;
- D3DXVECTOR3 m_vLookVector;
- D3DXVECTOR3 m_vCameraPosition;
- D3DXVECTOR3 m_vTargetPosition;
- D3DXMATRIX m_matView;
- D3DXMATRIX m_matProj;
- LPDIRECT3DDEVICE9 m_pd3dDevice;
-
- public:
-
- VOID CalculateViewMatrix(D3DXMATRIX *pMatrix);
-
-
- VOID GetProjMatrix(D3DXMATRIX *pMatrix) { *pMatrix = m_matProj; }
- VOID GetCameraPosition(D3DXVECTOR3 *pVector) { *pVector = m_vCameraPosition; }
- VOID GetLookVector(D3DXVECTOR3 *pVector) { *pVector = m_vLookVector; }
-
-
- VOID SetTargetPosition(D3DXVECTOR3 *pLookat = NULL);
- VOID SetCameraPosition(D3DXVECTOR3 *pVector = NULL);
- VOID SetViewMatrix(D3DXMATRIX *pMatrix = NULL);
- VOID SetProjMatrix(D3DXMATRIX *pMatrix = NULL);
-
- public:
-
- VOID MoveAlongRightVec(FLOAT fUnits);
- VOID MoveAlongUpVec(FLOAT fUnits);
- VOID MoveAlongLookVec(FLOAT fUnits);
-
-
- VOID RotationRightVec(FLOAT fAngle);
- VOID RotationUpVec(FLOAT fAngle);
- VOID RotationLookVec(FLOAT fAngle);
-
- public:
-
- CameraClass(IDirect3DDevice9 *pd3dDevice);
- virtual ~CameraClass(void);
-
- };
三、关于向量计算的六个函数讲解
下面我们得先介绍一下Direct3D中,与向量计算有关的这六个函数,在写这个类的过程中有用到。
Ⅰ.D3DXVec3Normalize函数
首先我们来介绍对向量进行规范化的D3DXVec3Normalize函数 ,在MSDN中我们查到它原型如下:
- D3DXVECTOR3* D3DXVec3Normalize(
- _Inout_ D3DXVECTOR3 *pOut,
- _In_ const D3DXVECTOR3 *pV
- );
这个函数的第一个参数为输出的结果,在第二个参数中填想要被规范化的向量就行了,一般我们把这两个参数填一摸一样的,就表示把填的这个向量规范化后的结果替代原来的向量。
举个例子就是这样写:
-
- D3DXVec3Normalize(&m_vLookVector, &m_vLookVector);
Ⅱ.D3DXVec3Cross函数
然后我们来介绍用于计算两个向量叉乘结果的D3DXVec3Cross函数,在MSDN中我们查到它原型如下:
- D3DXVECTOR3* D3DXVec3Cross(
- _Inout_ D3DXVECTOR3 *pOut,
- _In_ const D3DXVECTOR3 *pV1,
- _In_ const D3DXVECTOR3 *pV2
- );
第一个参数依然是计算的结果。第二和第三两个参数当然就是填参加叉乘运算的两个向量了。
另外需要注意,D3DXVec3Cross函数的返回值和第一个参数pOut 参数是一样的,为指向D3DXVECTOR3 结构的两个向量叉乘结果。
依然是一个实例:
- D3DXVec3Cross(&m_vRightVector, &m_vUpVector,&m_vLookVector);
Ⅲ.D3DXVec3Dot函数
下面我们来讲一下用于计算向量点乘的D3DXVec3Dot函数,在MSDN中我查到它的原型如下:
- FLOAT D3DXVec3Dot(
- _In_ const D3DXVECTOR3 *pV1,
- _In_ const D3DXVECTOR3 *pV2
- );
这个函数倒不是和我们上面刚讲的那两个函数一样有个用于存放结果的pOut,它的结果就存放在返回值中,而两个参数就填参与运算的两个向量。
举一个实例吧:
- pMatrix->_42 =-D3DXVec3Dot(&m_vUpVector, &m_vCameraPosition);
Ⅳ.D3DXMatrixRotationAxis函数
接下来介绍可以创建一个绕任意轴旋转一定角度的矩阵的D3DXMatrixRotationAxis函数。其函数原型如下:
- D3DXMATRIX* D3DXMatrixRotationAxis(
- _Inout_ D3DXMATRIX *pOut,
- _In_ const D3DXVECTOR3 *pV,
- _In_ FLOAT Angle
- );
第一个参数显然就填生成好的矩阵了,第二个参数填要绕着旋转的那根轴,第三个参数就填上要绕指定的轴旋转的角度。
依然是一个实例:
- D3DXMatrixRotationAxis(&R,&m_vRightVector, fAngle);
Ⅴ.D3DXVec3TransformCoord函数
下面我们看一个D3DXVec3TransformCoord函数,它可以根据给的的矩阵来变换一个向量,并且把变换后的向量规范化后输出来。这个函数原型如下:
- D3DXVECTOR3* D3DXVec3TransformCoord(
- _Inout_ D3DXVECTOR3 *pOut,
- _In_ const D3DXVECTOR3 *pV,
- _In_ const D3DXMATRIX *pM
- );
第一个参数就是得到的结果向量了。第二个参数填要被变换的那个向量,而第三个参数填用于变换的矩阵。
依然是一个实例:
- D3DXVec3TransformCoord(&m_vUpVector, &m_vCameraPosition, &R);
Ⅵ.D3DXVec3Length函数
最后我们介绍计算一个三维向量长度的D3DXVec3Length函数,这个函数原型如下:
- FLOAT D3DXVec3Length(
- _In_ const D3DXVECTOR3 *pV
- );
唯一的一个参数填要计算长度的那个向量,返回值就是计算出的给定向量的三维长度。
依然是一个实例:
- float length=D3DXVec3Length(&m_vCameraPosition);
四、计算取景变换矩阵
看完整个CameraClass类的轮廓,下面就准备开始讲解其中各个函数的实现代码应该怎么写。
首先就是我们最关心的取代了之前用D3DXMatrixLookAtLH无脑计算出取景变换矩阵,而自立门户的CalculateViewMatrix函数的写法。
为了讲解方便,我们令向量表示位置向量,
向量表示右向量,
向量表示上向量,
向量表示观察向量。
我们知道,取景变换所解决的其实就是世界坐标系中的物体在以摄像机为中心的坐标系中如何来表示的问题。这就是说,需要将世界坐标系中的物体随着摄像机一起进行变换,这样摄像机的坐标系就与世界坐标系完全重合了。
如下图所示:
上面的(a)图到(b)图,是一个平移的过程,而(b)图到(c)图则是一个旋转的过程。另外需要注意的一点是,空间中的物体也应该随着摄像机一同进行变换,这样摄像机中看到景物才没有变化。
我们的目的,就是通过一系列的矩阵变换,得到最终的取景变换矩阵V。
我们要得到取景变换矩阵V,说白了就是能够满足如下的条件:
pV=(0,0,0) 矩阵V将摄像机移动到世界坐标系的原点
rV=(1,0,0)矩阵V将摄像机的右向量与世界坐标系的x轴重合
uV=(0,1,0)矩阵V将摄像机的上向量与世界坐标系的y轴重合
lV=(0,0,1)矩阵V将摄像机的观察向量与世界坐标系的z轴重合
所以,想得到这个取景变换矩阵V,就是进行了先平移,后旋转,最后综合的三步曲操作,下面我们来对其各个击破:
1.平移
将摄像机的位置向量p平移到原点就是实现这个式子:pV=(0,0,0),即摄像机的位置分量乘以V之后等于(0,0,0)。那么很简单,我们假设取景变换矩阵V实现平移操作的的中间矩阵为T来简化一下。
而将摄像机的位置向量p平移到原点可以通过把他和它大小相等,方向相反的-p向量做加法轻松实现,即p-p=0,所以描述取景变换中的平移变换部分的T矩阵就是这样写:
2.旋转
想要让摄像机的各分量与世界坐标系的各轴重合的话,
即满足之前我们列出的这三个式子:
rV=(1,0,0)
uV=(0,1,0)
lV=(0,0,1)
实现起来,求出如下的一个3x3的矩阵A就可以了:
这里的矩阵A有很多种解法,最科学的解法在这里:
我们一眼就可以看出来A其实就是B矩阵的逆矩阵。而矩阵B刚好是个正交矩阵,我们文章前面就跟大家提到过要用到正交矩阵的这个性质:正交矩阵的逆矩阵等于其转置矩阵。用到我们这里,就是B矩阵的逆矩阵等于B矩阵的转置矩阵,而B矩阵的逆矩阵就是A矩阵,那么就是说A矩阵等于B矩阵的转置矩阵。
那么我们求一下B矩阵的转置矩阵,就是求出A矩阵了。
也就是:
3.综合前两步
我们需要把A矩阵扩展成4X4的,然后计算一下两矩阵的值TA=V就行了
即:
好了,取景变换矩阵V就被我们求出来了。
上面过程看不太懂不要紧,我们看一下最后V矩阵的结果就行了,下面我们实现计算取景变换矩阵的CalculateViewMatrix函数中,其实也就是用了一下最后我们求出的V矩阵的结果而已:
-
-
-
-
- VOID CameraClass::CalculateViewMatrix(D3DXMATRIX *pMatrix)
- {
-
- D3DXVec3Normalize(&m_vLookVector, &m_vLookVector);
- D3DXVec3Cross(&m_vUpVector, &m_vLookVector, &m_vRightVector);
- D3DXVec3Normalize(&m_vUpVector, &m_vUpVector);
- D3DXVec3Cross(&m_vRightVector, &m_vUpVector, &m_vLookVector);
- D3DXVec3Normalize(&m_vRightVector, &m_vRightVector);
-
-
-
-
- pMatrix->_11 = m_vRightVector.x;
- pMatrix->_12 = m_vUpVector.x;
- pMatrix->_13 = m_vLookVector.x;
- pMatrix->_14 = 0.0f;
-
- pMatrix->_21 = m_vRightVector.y;
- pMatrix->_22 = m_vUpVector.y;
- pMatrix->_23 = m_vLookVector.y;
- pMatrix->_24 = 0.0f;
-
- pMatrix->_31 = m_vRightVector.z;
- pMatrix->_32 = m_vUpVector.z;
- pMatrix->_33 = m_vLookVector.z;
- pMatrix->_34 = 0.0f;
-
- pMatrix->_41 = -D3DXVec3Dot(&m_vRightVector, &m_vCameraPosition);
- pMatrix->_42 = -D3DXVec3Dot(&m_vUpVector, &m_vCameraPosition);
- pMatrix->_43 = -D3DXVec3Dot(&m_vLookVector, &m_vCameraPosition);
- pMatrix->_44 = 1.0f;
- }
代码中浅墨已经注释很清楚了,我们先把3个向量都规范化并使其相互垂直,成为一组正交矩阵,然后对着我们计算出的取景变换矩阵V的结果,一行一行赋值就行了。
关于上面的赋值,我们随便抽一个出来讲一下:
比如这句:
- pMatrix->_23 = m_vLookVector.y;
其中的pMatrix->_23表示pMatrix矩阵的第二行,第三行的元素,我们在计算出的取景变换矩阵V的矩阵结果中找到第二行第三列,它的值为ly,也就是上向量m_vLookVector的y坐标值,即m_vLookVector.y,那么第二行第三列就是这样写了。
其他行其他列就以此类推了,注意的是一共要写4x4=16个值。
五、类的其余实现细节
因为这个类浅墨基本上把代码都逐行注释了,所以类中其他的函数的实现方法大家直接看代码就行了。另外在这个类中视口变换并没有去实现,其实很多时候不用去设置视口的Direct3D就为我们默认好了,不去设置也无伤大雅的。
CameraClass.cpp的所有代码如下(友情声明:头文件CameraClass.h头文件在上头我们已经贴出来过了):
-
-
-
-
-
- #include "CameraClass.h"
-
- #ifndef SCREEN_WIDTH
- #define SCREEN_WIDTH 800 //为窗口宽度定义的宏,以方便在此处修改窗口宽度
- #define SCREEN_HEIGHT 600 //为窗口高度定义的宏,以方便在此处修改窗口高度
- #endif
-
-
-
-
- CameraClass::CameraClass(IDirect3DDevice9 *pd3dDevice)
- {
- m_pd3dDevice = pd3dDevice;
- m_vRightVector = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
- m_vUpVector = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
- m_vLookVector = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
- m_vCameraPosition = D3DXVECTOR3(0.0f, 0.0f, -250.0f);
- m_vTargetPosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
-
- }
-
-
-
-
-
-
- VOID CameraClass::CalculateViewMatrix(D3DXMATRIX *pMatrix)
- {
-
- D3DXVec3Normalize(&m_vLookVector, &m_vLookVector);
- D3DXVec3Cross(&m_vUpVector, &m_vLookVector, &m_vRightVector);
- D3DXVec3Normalize(&m_vUpVector, &m_vUpVector);
- D3DXVec3Cross(&m_vRightVector, &m_vUpVector, &m_vLookVector);
- D3DXVec3Normalize(&m_vRightVector, &m_vRightVector);
-
-
-
-
- pMatrix->_11 = m_vRightVector.x;
- pMatrix->_12 = m_vUpVector.x;
- pMatrix->_13 = m_vLookVector.x;
- pMatrix->_14 = 0.0f;
-
- pMatrix->_21 = m_vRightVector.y;
- pMatrix->_22 = m_vUpVector.y;
- pMatrix->_23 = m_vLookVector.y;
- pMatrix->_24 = 0.0f;
-
- pMatrix->_31 = m_vRightVector.z;
- pMatrix->_32 = m_vUpVector.z;
- pMatrix->_33 = m_vLookVector.z;
- pMatrix->_34 = 0.0f;
-
- pMatrix->_41 = -D3DXVec3Dot(&m_vRightVector, &m_vCameraPosition);
- pMatrix->_42 = -D3DXVec3Dot(&m_vUpVector, &m_vCameraPosition);
- pMatrix->_43 = -D3DXVec3Dot(&m_vLookVector, &m_vCameraPosition);
- pMatrix->_44 = 1.0f;
- }
-
-
-
-
-
-
- VOID CameraClass::SetTargetPosition(D3DXVECTOR3 *pLookat)
- {
-
- if (pLookat != NULL) m_vTargetPosition = (*pLookat);
- else m_vTargetPosition = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
-
- m_vLookVector = m_vTargetPosition - m_vCameraPosition;
- D3DXVec3Normalize(&m_vLookVector, &m_vLookVector);
-
-
- D3DXVec3Cross(&m_vUpVector, &m_vLookVector, &m_vRightVector);
- D3DXVec3Normalize(&m_vUpVector, &m_vUpVector);
- D3DXVec3Cross(&m_vRightVector, &m_vUpVector, &m_vLookVector);
- D3DXVec3Normalize(&m_vRightVector, &m_vRightVector);
- }
-
-
-
-
-
- VOID CameraClass::SetCameraPosition(D3DXVECTOR3 *pVector)
- {
- D3DXVECTOR3 V = D3DXVECTOR3(0.0f, 0.0f, -250.0f);
- m_vCameraPosition = pVector ? (*pVector) : V;
- }
-
-
-
-
-
- VOID CameraClass::SetViewMatrix(D3DXMATRIX *pMatrix)
- {
-
- if (pMatrix) m_matView = *pMatrix;
- else CalculateViewMatrix(&m_matView);
- m_pd3dDevice->SetTransform(D3DTS_VIEW, &m_matView);
-
- m_vRightVector = D3DXVECTOR3(m_matView._11, m_matView._12, m_matView._13);
- m_vUpVector = D3DXVECTOR3(m_matView._21, m_matView._22, m_matView._23);
- m_vLookVector = D3DXVECTOR3(m_matView._31, m_matView._32, m_matView._33);
- }
-
-
-
-
-
- VOID CameraClass::SetProjMatrix(D3DXMATRIX *pMatrix)
- {
-
- if (pMatrix != NULL) m_matProj = *pMatrix;
- else D3DXMatrixPerspectiveFovLH(&m_matProj, D3DX_PI / 4.0f, (float)((double)SCREEN_WIDTH/SCREEN_HEIGHT), 1.0f, 30000.0f);
- m_pd3dDevice->SetTransform(D3DTS_PROJECTION, &m_matProj);
- }
-
-
-
-
-
- VOID CameraClass::MoveAlongRightVec(FLOAT fUnits)
- {
-
- m_vCameraPosition += m_vRightVector * fUnits;
- m_vTargetPosition += m_vRightVector * fUnits;
- }
-
-
-
-
-
- VOID CameraClass::MoveAlongUpVec(FLOAT fUnits)
- {
-
- m_vCameraPosition += m_vUpVector * fUnits;
- m_vTargetPosition += m_vUpVector * fUnits;
- }
-
-
-
-
-
- VOID CameraClass::MoveAlongLookVec(FLOAT fUnits)
- {
-
- m_vCameraPosition += m_vLookVector * fUnits;
- m_vTargetPosition += m_vLookVector * fUnits;
- }
-
-
-
-
-
- VOID CameraClass::RotationRightVec(FLOAT fAngle)
- {
- D3DXMATRIX R;
- D3DXMatrixRotationAxis(&R, &m_vRightVector, fAngle);
- D3DXVec3TransformCoord(&m_vUpVector, &m_vUpVector, &R);
- D3DXVec3TransformCoord(&m_vLookVector, &m_vLookVector, &R);
-
- m_vTargetPosition = m_vLookVector * D3DXVec3Length(&m_vCameraPosition);
- }
-
-
-
-
-
- VOID CameraClass::RotationUpVec(FLOAT fAngle)
- {
- D3DXMATRIX R;
- D3DXMatrixRotationAxis(&R, &m_vUpVector, fAngle);
- D3DXVec3TransformCoord(&m_vRightVector, &m_vRightVector, &R);
- D3DXVec3TransformCoord(&m_vLookVector, &m_vLookVector, &R);
-
- m_vTargetPosition = m_vLookVector * D3DXVec3Length(&m_vCameraPosition);
- }
-
-
-
-
-
- VOID CameraClass::RotationLookVec(FLOAT fAngle)
- {
- D3DXMATRIX R;
- D3DXMatrixRotationAxis(&R, &m_vLookVector, fAngle);
- D3DXVec3TransformCoord(&m_vRightVector, &m_vRightVector, &R);
- D3DXVec3TransformCoord(&m_vUpVector, &m_vUpVector, &R);
-
- m_vTargetPosition = m_vLookVector * D3DXVec3Length(&m_vCameraPosition);
- }
-
-
-
-
-
- CameraClass::~CameraClass(void)
- {
- }
怎么样,注释够详细吧,类的实现代码其实就是对着写好的头文件挨着挨着把函数扩展实现出来就OK了。
这个类的使用方面的话,我们一般就是在在给绘制做准备的Objects_Init()函数中调用一下,这就是这样子写:
-
- g_pCamera = new CameraClass(g_pd3dDevice);
- g_pCamera->SetCameraPosition(&D3DXVECTOR3(0.0f, 200.0f, -300.0f));
- g_pCamera->SetTargetPosition(&D3DXVECTOR3(0.0f, 300.0f, 0.0f));
- g_pCamera->SetViewMatrix();
- g_pCamera->SetProjMatrix();
好了,看完摄像机类的实现,下面我们就放出这个综合性非常强的“场景漫游”示例程序。
五、详细注释的源代码欣赏
在为大家写这个示例程序的时候,浅墨感觉自己在创造整个世界,非常地有成就感。首先是选择天蓝色的清屏颜色,这样我们的“世界”看起来就像是在蓝天晴空的背景下了。然后是选择翠绿色的可以无缝衔接的草坪纹理,用之前学的纹理映射和顶点缓存的知识把纹理重复铺上个几百张(具体张数是50x 50=250张),这样就是一大片绿油油的草坪了。接着就是我们几节之前刚讲的载入X文件模型,浅墨选的人物模型是PS3版《真三国无双6》中的王元姬,一袭蓝色的衣服,非常好看。再接着我们还用D3DXCreateCylinder函数创建了一根柱子(后面用for循环绘制了12根一摸一样的柱子),最后就是设置光照,设置渲染状态什么的。
先是用的纹理素材: 尺寸是512x512
然后是PS3版《真三国无双6》中的王元姬在3DS Max中的渲染效果图:
也先放一张程序的运行截图:
本篇文章的配套源代码包含了6个文件,主要用于公共辅助宏定义的D3DUtil.h,用于封装了DirectInput输入控制API的DirectInputClass.h和DirectInputClass.cpp,以及封装了虚拟摄像机类的CameraClass.h和CameraClass.cpp,当还还有核心代码main.cpp。
DirectInputClass.h和DirectInputClass.cpp较之前的文章中依然没有任何修改,依然不再贴出,CameraClass.cpp和CameraClass.h在上面讲解的过程中以及贴出来了,这里也不贴出,我们依然只贴核心代码main.cpp,大家要看得爽的话,源代码在文章末尾有下载链接,下回去用VisualStuido看就行了。下面就是main.cpp的全部代码:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #define SCREEN_WIDTH 800 //为窗口宽度定义的宏,以方便在此处修改窗口宽度
- #define SCREEN_HEIGHT 600 //为窗口高度定义的宏,以方便在此处修改窗口高度
- #define WINDOW_TITLE _T("【Visual C++】游戏开发笔记系列配套示例程序四十七 浅墨DirectX教程十五 翱翔于三维空间:第一人称摄像机的实现") //为窗口标题定义的宏
-
-
-
-
-
-
- #include <d3d9.h>
- #include <d3dx9.h>
- #include <tchar.h>
- #include <time.h>
- #include "DirectInputClass.h"
- #include "CameraClass.h"
-
-
-
-
-
- #pragma comment(lib,"d3d9.lib")
- #pragma comment(lib,"d3dx9.lib")
- #pragma comment(lib, "dinput8.lib") // 使用DirectInput必须包含的库文件,注意这里有8
- #pragma comment(lib,"dxguid.lib")
- #pragma comment(lib, "winmm.lib")
-
-
-
- struct CUSTOMVERTEX
- {
- FLOAT _x, _y, _z;
- FLOAT _nx, _ny, _nz;
- FLOAT _u, _v;
- CUSTOMVERTEX(FLOAT x, FLOAT y, FLOAT z,
- FLOAT nx, FLOAT ny, FLOAT nz, FLOAT u, FLOAT v)
- {
- _x = x, _y = y, _z = z;
- _nx = nx, _ny = ny, _nz = nz;
- _u = u, _v = v;
- }
- };
- #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
-
-
-
-
-
- LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
- LPD3DXFONT g_pTextFPS =NULL;
- LPD3DXFONT g_pTextAdaperName = NULL;
- LPD3DXFONT g_pTextHelper = NULL;
- LPD3DXFONT g_pTextInfor = NULL;
- float g_FPS = 0.0f;
- wchar_t g_strFPS[50]={0};
- wchar_t g_strAdapterName[60]={0};
- D3DXMATRIX g_matWorld;
- DInputClass* g_pDInput = NULL;
- CameraClass* g_pCamera = NULL;
- LPD3DXMESH g_pMesh = NULL;
- D3DMATERIAL9* g_pMaterials = NULL;
- LPDIRECT3DTEXTURE9* g_pTextures = NULL;
- DWORD g_dwNumMtrls = 0;
- LPD3DXMESH g_cylinder = NULL;
- D3DMATERIAL9 g_MaterialCylinder;
- LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer = NULL;
- LPDIRECT3DTEXTURE9 g_pTexture = NULL;
-
-
-
-
-
- LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
- HRESULT Direct3D_Init(HWND hwnd,HINSTANCE hInstance);
- HRESULT Objects_Init();
- void Direct3D_Render( HWND hwnd);
- void Direct3D_Update( HWND hwnd);
- void Direct3D_CleanUp( );
- float Get_FPS();
- void HelpText_Render(HWND hwnd);
-
-
-
-
-
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
- {
-
-
- WNDCLASSEX wndClass = { 0 };
- wndClass.cbSize = sizeof( WNDCLASSEX ) ;
- wndClass.style = CS_HREDRAW | CS_VREDRAW;
- wndClass.lpfnWndProc = WndProc;
- wndClass.cbClsExtra = 0;
- wndClass.cbWndExtra = 0;
- wndClass.hInstance = hInstance;
- wndClass.hIcon=(HICON)::LoadImage(NULL,_T("icon.ico"),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);
- wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );
- wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);
- wndClass.lpszMenuName = NULL;
- wndClass.lpszClassName = _T("ForTheDreamOfGameDevelop");
-
- if( !RegisterClassEx( &wndClass ) )
- return -1;
-
- HWND hwnd = CreateWindow( _T("ForTheDreamOfGameDevelop"),WINDOW_TITLE,
- WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, SCREEN_WIDTH,
- SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );
-
-
-
- if (!(S_OK==Direct3D_Init (hwnd,hInstance)))
- {
- MessageBox(hwnd, _T("Direct3D初始化失败~!"), _T("浅墨的消息窗口"), 0);
- }
- PlaySound(L"雅尼 - 兰花.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP);
-
-
-
- MoveWindow(hwnd,200,50,SCREEN_WIDTH,SCREEN_HEIGHT,true);
- ShowWindow( hwnd, nShowCmd );
- UpdateWindow(hwnd);
-
-
- g_pDInput = new DInputClass();
- g_pDInput->Init(hwnd,hInstance,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
-
-
- MSG msg = { 0 };
- while( msg.message != WM_QUIT )
- {
- if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
- {
- TranslateMessage( &msg );
- DispatchMessage( &msg );
- }
- else
- {
- Direct3D_Update(hwnd);
- Direct3D_Render(hwnd);
- }
- }
-
- UnregisterClass(_T("ForTheDreamOfGameDevelop"), wndClass.hInstance);
- return 0;
- }
-
-
-
-
-
-
-
- LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
- {
- switch( message )
- {
- case WM_PAINT:
- Direct3D_Render(hwnd);
- ValidateRect(hwnd, NULL);
- break;
-
- case WM_KEYDOWN:
- if (wParam == VK_ESCAPE)
- DestroyWindow(hwnd);
- break;
- case WM_DESTROY:
- Direct3D_CleanUp();
- PostQuitMessage( 0 );
- break;
-
- default:
- return DefWindowProc( hwnd, message, wParam, lParam );
- }
-
- return 0;
- }
-
-
-
-
-
-
-
-
-
-
-
-
- HRESULT Direct3D_Init(HWND hwnd,HINSTANCE hInstance)
- {
-
-
-
-
- LPDIRECT3D9 pD3D = NULL;
- if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
- return E_FAIL;
-
-
-
-
- D3DCAPS9 caps; int vp = 0;
- if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) )
- {
- return E_FAIL;
- }
- if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
- vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
- else
- vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
-
-
-
-
- D3DPRESENT_PARAMETERS d3dpp;
- ZeroMemory(&d3dpp, sizeof(d3dpp));
- d3dpp.BackBufferWidth = SCREEN_WIDTH;
- d3dpp.BackBufferHeight = SCREEN_HEIGHT;
- d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
- d3dpp.BackBufferCount = 2;
- d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
- d3dpp.MultiSampleQuality = 0;
- d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
- d3dpp.hDeviceWindow = hwnd;
- d3dpp.Windowed = true;
- d3dpp.EnableAutoDepthStencil = true;
- d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
- d3dpp.Flags = 0;
- d3dpp.FullScreen_RefreshRateInHz = 0;
- d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
-
-
-
-
- if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
- hwnd, vp, &d3dpp, &g_pd3dDevice)))
- return E_FAIL;
-
-
-
- wchar_t TempName[60]=L"当前显卡型号:";
- D3DADAPTER_IDENTIFIER9 Adapter;
- pD3D->GetAdapterIdentifier(0,0,&Adapter);
- int len = MultiByteToWideChar(CP_ACP,0, Adapter.Description, -1, NULL, 0);
- MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, g_strAdapterName, len);
- wcscat_s(TempName,g_strAdapterName);
- wcscpy_s(g_strAdapterName,TempName);
-
- if(!(S_OK==Objects_Init())) return E_FAIL;
-
- SAFE_RELEASE(pD3D)
-
- return S_OK;
- }
-
-
- HRESULT Objects_Init()
- {
-
- D3DXCreateFont(g_pd3dDevice, 36, 0, 0, 1000, false, DEFAULT_CHARSET,
- OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T("Calibri"), &g_pTextFPS);
- D3DXCreateFont(g_pd3dDevice, 20, 0, 1000, 0, false, DEFAULT_CHARSET,
- OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"华文中宋", &g_pTextAdaperName);
- D3DXCreateFont(g_pd3dDevice, 23, 0, 1000, 0, false, DEFAULT_CHARSET,
- OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"微软雅黑", &g_pTextHelper);
- D3DXCreateFont(g_pd3dDevice, 26, 0, 1000, 0, false, DEFAULT_CHARSET,
- OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"黑体", &g_pTextInfor);
-
-
- LPD3DXBUFFER pAdjBuffer = NULL;
- LPD3DXBUFFER pMtrlBuffer = NULL;
-
- D3DXLoadMeshFromX(L"WYJ.X", D3DXMESH_MANAGED, g_pd3dDevice,
- &pAdjBuffer, &pMtrlBuffer, NULL, &g_dwNumMtrls, &g_pMesh);
-
-
- D3DXMATERIAL *pMtrls = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer();
- g_pMaterials = new D3DMATERIAL9[g_dwNumMtrls];
- g_pTextures = new LPDIRECT3DTEXTURE9[g_dwNumMtrls];
-
- for (DWORD i=0; i<g_dwNumMtrls; i++)
- {
-
- g_pMaterials[i] = pMtrls[i].MatD3D;
- g_pMaterials[i].Ambient = g_pMaterials[i].Diffuse;
-
-
- g_pTextures[i] = NULL;
- D3DXCreateTextureFromFileA(g_pd3dDevice, pMtrls[i].pTextureFilename, &g_pTextures[i]);
- }
- SAFE_RELEASE(pAdjBuffer)
- SAFE_RELEASE(pMtrlBuffer)
-
-
-
- g_pd3dDevice->CreateVertexBuffer(4 * sizeof(CUSTOMVERTEX), 0,
- D3DFVF_CUSTOMVERTEX, D3DPOOL_MANAGED, &g_pVertexBuffer, 0);
-
- CUSTOMVERTEX *pVertices = NULL;
- g_pVertexBuffer->Lock(0, 0, (void**)&pVertices, 0);
- pVertices[0] = CUSTOMVERTEX(-500.0f, 0.0f, -500.0f, 0.0f, 1.0f, 0.0f, 0.0f, 50.0f);
- pVertices[1] = CUSTOMVERTEX(-500.0f, 0.0f, 500.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
- pVertices[2] = CUSTOMVERTEX( 500.0f, 0.0f, -500.0f, 0.0f, 1.0f, 0.0f, 50.0f, 50.0f);
- pVertices[3] = CUSTOMVERTEX( 500.0f, 0.0f, 500.0f, 0.0f, 1.0f, 0.0f, 50.0f, 0.0f);
- g_pVertexBuffer->Unlock();
-
-
- D3DXCreateTextureFromFile(g_pd3dDevice, L"grass.jpg", &g_pTexture);
-
-
- D3DXCreateCylinder(g_pd3dDevice, 10.0f, 10.0f, 500.0f, 60, 60, &g_cylinder, 0);
- g_MaterialCylinder.Ambient = D3DXCOLOR(0.9f, 0.0f, 0.8f, 1.0f);
- g_MaterialCylinder.Diffuse = D3DXCOLOR(0.9f, 0.0f, 0.8f, 1.0f);
- g_MaterialCylinder.Specular = D3DXCOLOR(0.9f, 0.2f, 0.9f, 0.9f);
- g_MaterialCylinder.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.9f, 1.0f);
-
-
- D3DLIGHT9 light;
- ::ZeroMemory(&light, sizeof(light));
- light.Type = D3DLIGHT_DIRECTIONAL;
- light.Ambient = D3DXCOLOR(0.7f, 0.7f, 0.7f, 1.0f);
- light.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
- light.Specular = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);
- light.Direction = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
- g_pd3dDevice->SetLight(0, &light);
- g_pd3dDevice->LightEnable(0, true);
- g_pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true);
- g_pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, true);
-
-
- g_pCamera = new CameraClass(g_pd3dDevice);
- g_pCamera->SetCameraPosition(&D3DXVECTOR3(0.0f, 200.0f, -300.0f));
- g_pCamera->SetTargetPosition(&D3DXVECTOR3(0.0f, 300.0f, 0.0f));
- g_pCamera->SetViewMatrix();
- g_pCamera->SetProjMatrix();
-
-
- g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
- g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
- g_pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
-
- return S_OK;
- }
-
- void Direct3D_Update( HWND hwnd)
- {
-
- g_pDInput->GetInput();
-
-
- if (g_pDInput->IsKeyDown(DIK_A)) g_pCamera->MoveAlongRightVec(-0.3f);
- if (g_pDInput->IsKeyDown(DIK_D)) g_pCamera->MoveAlongRightVec( 0.3f);
- if (g_pDInput->IsKeyDown(DIK_W)) g_pCamera->MoveAlongLookVec( 0.3f);
- if (g_pDInput->IsKeyDown(DIK_S)) g_pCamera->MoveAlongLookVec(-0.3f);
- if (g_pDInput->IsKeyDown(DIK_I)) g_pCamera->MoveAlongUpVec( 0.3f);
- if (g_pDInput->IsKeyDown(DIK_K)) g_pCamera->MoveAlongUpVec(-0.3f);
-
-
- if (g_pDInput->IsKeyDown(DIK_LEFT)) g_pCamera->RotationUpVec(-0.003f);
- if (g_pDInput->IsKeyDown(DIK_RIGHT)) g_pCamera->RotationUpVec( 0.003f);
- if (g_pDInput->IsKeyDown(DIK_UP)) g_pCamera->RotationRightVec(-0.003f);
- if (g_pDInput->IsKeyDown(DIK_DOWN)) g_pCamera->RotationRightVec( 0.003f);
- if (g_pDInput->IsKeyDown(DIK_J)) g_pCamera->RotationLookVec(-0.001f);
- if (g_pDInput->IsKeyDown(DIK_L)) g_pCamera->RotationLookVec( 0.001f);
-
-
- g_pCamera->RotationUpVec(g_pDInput->MouseDX()* 0.001f);
- g_pCamera->RotationRightVec(g_pDInput->MouseDY() * 0.001f);
-
-
- static FLOAT fPosZ=0.0f;
- fPosZ += g_pDInput->MouseDZ()*0.03f;
-
-
- D3DXMATRIX matView;
- g_pCamera->CalculateViewMatrix(&matView);
- g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
-
-
- D3DXMatrixTranslation(&g_matWorld, 0.0f, 0.0f, fPosZ);
-
-
- POINT lt,rb;
- RECT rect;
- GetClientRect(hwnd,&rect);
-
- lt.x = rect.left;
- lt.y = rect.top;
-
- rb.x = rect.right;
- rb.y = rect.bottom;
-
- ClientToScreen(hwnd,<);
- ClientToScreen(hwnd,&rb);
-
- rect.left = lt.x;
- rect.top = lt.y;
- rect.right = rb.x;
- rect.bottom = rb.y;
-
- ClipCursor(&rect);
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- void Direct3D_Render(HWND hwnd)
- {
-
-
-
-
- g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, D3DCOLOR_XRGB(50, 100, 250), 1.0f, 0);
-
-
-
-
- g_pd3dDevice->BeginScene();
-
-
-
-
-
-
- g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_matWorld);
-
- for (DWORD i = 0; i < g_dwNumMtrls; i++)
- {
- g_pd3dDevice->SetMaterial(&g_pMaterials[i]);
- g_pd3dDevice->SetTexture(0, g_pTextures[i]);
- g_pMesh->DrawSubset(i);
- }
-
-
- D3DXMATRIX matWorld;
- D3DXMatrixTranslation(&matWorld, 0.0f, 0.0f, 0.0f);
- g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);
- g_pd3dDevice->SetTexture(0, g_pTexture);
- g_pd3dDevice->SetStreamSource(0, g_pVertexBuffer, 0, sizeof(CUSTOMVERTEX));
- g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
- g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
-
-
- D3DXMATRIX TransMatrix, RotMatrix, FinalMatrix;
- D3DXMatrixRotationX(&RotMatrix, -D3DX_PI * 0.5f);
- g_pd3dDevice->SetMaterial(&g_MaterialCylinder);
- for(int i = 0; i < 6; i++)
- {
- D3DXMatrixTranslation(&TransMatrix, -100.0f, 0.0f, -150.0f + (i * 75.0f));
- FinalMatrix = RotMatrix * TransMatrix ;
- g_pd3dDevice->SetTransform(D3DTS_WORLD, &FinalMatrix);
- g_cylinder->DrawSubset(0);
-
- D3DXMatrixTranslation(&TransMatrix, 100.0f, 0.0f, -150.0f + (i * 75.0f));
- FinalMatrix = RotMatrix * TransMatrix ;
- g_pd3dDevice->SetTransform(D3DTS_WORLD, &FinalMatrix);
- g_cylinder->DrawSubset(0);
- }
-
- HelpText_Render(hwnd);
-
-
-
-
-
- g_pd3dDevice->EndScene();
-
-
-
- g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
-
- }
-
-
- void HelpText_Render(HWND hwnd)
- {
-
- RECT formatRect;
- GetClientRect(hwnd, &formatRect);
-
-
- formatRect.top = 5;
- int charCount = swprintf_s(g_strFPS, 20, _T("FPS:%0.3f"), Get_FPS() );
- g_pTextFPS->DrawText(NULL, g_strFPS, charCount , &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_RGBA(0,239,136,255));
-
-
- g_pTextAdaperName->DrawText(NULL,g_strAdapterName, -1, &formatRect,
- DT_TOP | DT_LEFT, D3DXCOLOR(1.0f, 0.5f, 0.0f, 1.0f));
-
-
- formatRect.left = 0,formatRect.top = 380;
- g_pTextInfor->DrawText(NULL, L"控制说明:", -1, &formatRect,
- DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(235,123,230,255));
- formatRect.top += 35;
- g_pTextHelper->DrawText(NULL, L" W:向前飞翔 S:向后飞翔 ", -1, &formatRect,
- DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
- formatRect.top += 25;
- g_pTextHelper->DrawText(NULL, L" A:向左飞翔 D:向右飞翔", -1, &formatRect,
- DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
- formatRect.top += 25;
- g_pTextHelper->DrawText(NULL, L" I:垂直向上飞翔 K:垂直向下飞翔", -1, &formatRect,
- DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
- formatRect.top += 25;
- g_pTextHelper->DrawText(NULL, L" J:向左倾斜 L:向右倾斜", -1, &formatRect,
- DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
- formatRect.top += 25;
- g_pTextHelper->DrawText(NULL, L" 上、下、左、右方向键、鼠标移动:视角变化 ", -1, &formatRect,
- DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
- formatRect.top += 25;
- g_pTextHelper->DrawText(NULL, L" 鼠标滚轮:人物模型Y轴方向移动", -1, &formatRect,
- DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
- formatRect.top += 25;
- g_pTextHelper->DrawText(NULL, L" ESC键 : 退出程序", -1, &formatRect,
- DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
- }
-
-
-
-
-
- float Get_FPS()
- {
-
-
- static float fps = 0;
- static int frameCount = 0;
- static float currentTime =0.0f;
- static float lastTime = 0.0f;
-
- frameCount++;
- currentTime = timeGetTime()*0.001f;
-
-
- if(currentTime - lastTime > 1.0f)
- {
- fps = (float)frameCount /(currentTime - lastTime);
- lastTime = currentTime;
- frameCount = 0;
- }
-
- return fps;
- }
-
-
-
-
-
-
-
- void Direct3D_CleanUp()
- {
-
-
- for (DWORD i = 0; i<g_dwNumMtrls; i++)
- SAFE_RELEASE(g_pTextures[i]);
- SAFE_DELETE(g_pTextures);
- SAFE_DELETE(g_pMaterials);
- SAFE_DELETE(g_pDInput);
- SAFE_RELEASE(g_cylinder);
- SAFE_RELEASE(g_pMesh);
- SAFE_RELEASE(g_pd3dDevice);
- SAFE_RELEASE(g_pTextAdaperName)
- SAFE_RELEASE(g_pTextHelper)
- SAFE_RELEASE(g_pTextInfor)
- SAFE_RELEASE(g_pTextFPS)
- SAFE_RELEASE(g_pd3dDevice)
- }
本篇文章的示例程序相对来说比较综合,里面融合了Direct3D的初始化,DirectInput输入处理,顶点缓存,文字输出,颜色,光照与材质,纹理贴图,四大变换,网格模型,X文件载入等等知识(当然还有默认被开启的深度缓存)。
虽然这听起来怎么这么地,但是却是很好理解的,就是在HRESULT Objects_Init()函数中把要绘制的物体都准备好,也把光照、材质、摄像机都初始化好,然后在Direct3D_Render( HWND hwnd);函数中绘制出来就可以了。说白了也就是相对于之前的demo中多绘制了一点东西而已,没什么特别的。
摄像机类的使用的话,就是在Objects_Init()顺带着中初始化一下,然后在Direct3D_Update( HWND hwnd)函数中配合着DirectInput类对输出进行处理就好了。
另外需要注意的是,我们把文字帮助信息和FPS的输出都封装在了一个叫HelpText_Render的函数中了。因为我们绘制的东西越来越多,为了Direct3D_Render看起来整洁一点,而不是里面大一串内容,是时候把文字帮助信息封装起来了。
下面来看一下运行截图,这次的运行效果就可以天马行空了,可以在无限的空间中任意地遨游,想去哪儿就飞去哪儿。
程序运行后就是这样,同时一曲唯美的出自轻音乐大师雅尼的《兰花》映入耳帘:
我们开始在键盘上W,A,S,D,I,J,K,L,↑,↓,←,→12个键乱按,加上鼠标乱晃:
越飞越远:
摄像机在蓝天之下,草原之上盘旋:
来张正面照:
暮然回首,那人却在灯火阑珊处:
隔近了看草地就是这样的:
这一袭蓝衣真美:
更多图片就不贴了,大家在文章末尾下源代码自己拿回去玩去吧~
另外我们在Direct3D_Update函数中涉及到了将鼠标限制在某一区域之内的写法,这个其实很好实现的,我们在讲解Windows鼠标消息处理的那篇文章中用到过了。
细心的朋友们应该可以发现,这个程序运行起来还是有一点小问题的,当我们对摄像机进行旋转操作时,视图有时候会发生倾斜,如果对于飞行射击游戏,这样的倾斜是完全没问题的,但是如果是做CF,CS,鬼泣这类的第一人称的游戏的话,那就不应该发生这样的视觉倾斜了。
摄像机之所以发生倾斜是因为我们构造的摄像机的几个分量是两两之间互相垂直的。要想解决这样的问题,就需要保证每次进行摄像机的左右旋转时,是绕着垂直于世界坐标系的轴的向量,而这根轴的向量就是世界坐标系的Y轴。这是我们以后待完成的工作了。
文章最后,依旧是放出本篇文章配套源代码的下载:
本节笔记配套源代码请点击这里下载:
【浅墨DirectX提高班】配套源代码之十五下载
以上就是本节笔记的全部内容,更多精彩内容,且听下回分解。
浅墨在这里,希望喜欢游戏开发系列文章的朋友们能留下你们的评论,每次浅墨登陆博客看到大家的留言的时候都会非常开心,感觉自己正在传递一种信仰,一种精神。
文章最后,依然是【每文一语】栏目,今天的句子是:
我们所有的梦想都可以成真,只要我们有勇气去追求它们。
All our dreams can come true, if we have the courageto pursue them.
————沃尔特·迪斯尼
下周一,让我们离游戏开发的梦想更近一步。
下周一,游戏开发笔记,我们,不见不散。