【游戏程序设计】Direct 3D第一人称摄像机
因为浅墨的摄像机有镜头倾斜的问题,根据评论对其做了一些改变,最终可以让镜头不倾斜了。
具体改动的函数是绕上向量旋转的函数。将绕上向量旋转改为绕Y轴旋转即可。因为根据人的视觉习惯是绕Y轴旋转的。
//-----------------------------------------------------------------------------
//沿上分量旋转fAngle个弧度单位的角度
//-----------------------------------------------------------------------------
void CameraClass::RotationUpVec(float fAngle)
{
D3DXMATRIX R;
D3DXMatrixRotationY(&R, fAngle); //创建出绕Y轴旋转fAngle个单位的R矩阵
D3DXVec3TransformCoord(&m_vUpVector, &m_vUpVector, &R); //让m_vUpVector向量绕Y轴旋转fAngle个角度
D3DXVec3TransformCoord(&m_vRightVector, &m_vRightVector, &R); //让m_vRightVector向量绕Y轴旋转fAngle个角度
D3DXVec3TransformCoord(&m_vLookVector, &m_vLookVector, &R); //让m_vLookVector向量绕Y轴旋转fAngle个角度
float length = D3DXVec3Length(&(m_vTargetPosition - m_vCameraPosition));
m_vTargetPosition = m_vCameraPosition + m_vLookVector * length; //更新一下观察点的新位置
}
运行结果:
源代码Camera.h:
//*****************************************************************************************
//封装了虚拟摄像机类的头文件
//*****************************************************************************************
#pragma once
#include <d3d9.h>
#include <d3dx9.h>
#include "D3DUtil.h"
class CameraClass
{
private:
//成员变量的声明
D3DXVECTOR3 m_vRightVector; //右分量向量
D3DXVECTOR3 m_vUpVector; //上分量向量
D3DXVECTOR3 m_vLookVector; //观察方向分量
D3DXVECTOR3 m_vCameraPosition; //摄像机位置的向量
D3DXVECTOR3 m_vTargetPosition; //目标观察位置的向量
D3DXMATRIX matView; //取景变换矩阵
D3DXMATRIX matProj; //投影变换矩阵
LPDIRECT3DDEVICE9 m_pd3dDevice; //Direct3D设备对象
private:
void CalculateViewMatrix(D3DMATRIX *pMatrix); //计算取景变换矩阵
void VectorNormalize(); //先把3个向量都规范化并使其相互垂直,成为一组正交矩阵
public:
//构造函数和析构函数
CameraClass(LPDIRECT3DDEVICE9 pd3dDevice); //构造函数
~CameraClass(void); //析构函数
//三个Get系列函数
void GetProjMatrix(D3DMATRIX *pMatrix) {*pMatrix = matProj;} //返回当前投影矩阵
void GetCameraPosition(D3DXVECTOR3 *pVector){*pVector = m_vCameraPosition;} //返回当前摄像机位置向量
void GetLookVector(D3DXVECTOR3 *pVector){*pVector = m_vLookVector;} //返回当前的观察向量
//四个Set系列函数,注意他们参数都有默认值NULL的,调用时不写参数也可以
void SetTargetPosition(D3DXVECTOR3 *pLookAt = NULL); //设置摄像机的目标观察向量
void SetCameraPosition(D3DXVECTOR3 *pVector = NULL); //设置摄像机所在的位置
void SetViewMatrix(D3DMATRIX *pMatrix = NULL); //设置投影变换矩阵
void SetProjMatrix(D3DMATRIX *pMatrix = NULL); //设置投影变换矩阵
//沿各分量平移的三个函数
void MoveAlongRightVec(float fUnits); //沿right向量移动
void MoveAlongUpVec(float fUnits); //沿up向量移动
void MoveAlongLookVec(float fUnits); //沿look向量移动
//沿各分量旋转的三个函数
void RotationRightVec(float fAngle); //绕right分量选择
void RotationUpVec(float fAngle); //绕up向量旋转
void RotationLookVec(float fAngle); //绕look向量旋转
};
Camera.cpp:
//=============================================================================
//封装了实现虚拟摄像机类的源文件
//=============================================================================
#include "CameraClass.h"
//-----------------------------------------------------------------------------
//构造函数
//-----------------------------------------------------------------------------
CameraClass::CameraClass(LPDIRECT3DDEVICE9 pd3dDevice)
{
m_pd3dDevice = pd3dDevice;
m_vRightVector = D3DXVECTOR3(1.0f, 0.0f, 0.0f); //默认右向量与X正半轴重合
m_vUpVector = D3DXVECTOR3(0.0f, 1.0f, 0.0f); //默认上向量与Y正半轴重合
m_vLookVector = D3DXVECTOR3(0.0f, 0.0f, 1.0f); //默认观察向量与Z正半轴重合
m_vCameraPosition = D3DXVECTOR3(0.0f, 0.0f, -250.0f); //默认相机坐标为(0.0f, 0.0f, -250.0f)
m_vTargetPosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f); //默认观察坐标为(0.0f, 0.0f, 0.0f)
}
//根据给定的矩阵计算出取景变换矩阵
void CameraClass::CalculateViewMatrix(D3DMATRIX *pMatrix)
{
//创建出取景变换矩阵
//依次写出取景变换矩阵的第一行
pMatrix->_11 = m_vRightVector.x; // Rx
pMatrix->_12 = m_vUpVector.x; // Ux
pMatrix->_13 = m_vLookVector.x; // Lx
pMatrix->_14 = 0.0f;
//依次写出取景变换矩阵的第二行
pMatrix->_21 = m_vRightVector.y; // Ry
pMatrix->_22 = m_vUpVector.y; // Uy
pMatrix->_23 = m_vLookVector.y; // Ly
pMatrix->_24 = 0.0f;
//依次写出取景变换矩阵的第三行
pMatrix->_31 = m_vRightVector.z; // Rz
pMatrix->_32 = m_vUpVector.z; // Uz
pMatrix->_33 = m_vLookVector.z; // Lz
pMatrix->_34 = 0.0f;
//依次写出取景变换矩阵的第四行
pMatrix->_41 = -D3DXVec3Dot(&m_vRightVector, &m_vCameraPosition); // -P*R
pMatrix->_42 = -D3DXVec3Dot(&m_vUpVector, &m_vCameraPosition); // -P*U
pMatrix->_43 = -D3DXVec3Dot(&m_vLookVector, &m_vCameraPosition); // -P*L
pMatrix->_44 = 1.0f;
}
//-----------------------------------------------------------------------------
//先把3个向量都规范化并使其相互垂直,成为一组正交矩阵
//-----------------------------------------------------------------------------
void CameraClass::VectorNormalize()
{
D3DXVec3Cross(&m_vUpVector, &m_vLookVector, &m_vRightVector); //上向量与观察向量垂直
D3DXVec3Normalize(&m_vUpVector, &m_vUpVector); //规范化上向量
}
//-----------------------------------------------------------------------------
//设置摄像机的观察位置
//-----------------------------------------------------------------------------
void CameraClass::SetTargetPosition(D3DXVECTOR3 *pLookAt)
{
//先看看pLookat是否为默认值NULL
if(pLookAt)
m_vTargetPosition = *pLookAt;
m_vLookVector = m_vTargetPosition - m_vCameraPosition; //观察点位置减摄像机目标位置,得到观察方向向量
D3DXVec3Normalize(&m_vLookVector, &m_vLookVector); //规范化m_vLookVector向量
VectorNormalize(); //正交并规范化三个向量
}
//-----------------------------------------------------------------------------
//设置摄像机的位置
//-----------------------------------------------------------------------------
void CameraClass::SetCameraPosition(D3DXVECTOR3 *pVector)
{
//先看看pVector是否为默认值NULL
if(pVector)
m_vCameraPosition = *pVector;
m_vLookVector = m_vTargetPosition - m_vCameraPosition; //观察点位置减摄像机目标位置,得到观察方向向量
D3DXVec3Normalize(&m_vLookVector, &m_vLookVector); //规范化m_vLookVector向量
VectorNormalize(); //正交并规范化三个向量
}
//-----------------------------------------------------------------------------
//设置取景变换矩阵
//-----------------------------------------------------------------------------
void CameraClass::SetViewMatrix(D3DMATRIX *pMatrix)
{
//根据pMatrix的值先做一下判断
if(pMatrix)
matView = *pMatrix;
else
CalculateViewMatrix(&matView);
//变换矩阵的值分下来分别给右向量,上向量,和观察向量,并规范化他们
m_vRightVector = D3DXVECTOR3(matView._11, matView._21, matView._31);
D3DXVec3Normalize(&m_vRightVector, &m_vRightVector);
m_vUpVector = D3DXVECTOR3(matView._12, matView._22, matView._32);
D3DXVec3Normalize(&m_vUpVector, &m_vUpVector);
m_vLookVector = D3DXVECTOR3(matView._13, matView._23, matView._33);
D3DXVec3Normalize(&m_vLookVector, &m_vLookVector);
m_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
}
//-----------------------------------------------------------------------------
//设置投影变换矩阵
//-----------------------------------------------------------------------------
void CameraClass::SetProjMatrix(D3DMATRIX *pMatrix)
{
//判断值有没有,没有就计算一下
if(pMatrix)
matProj = *pMatrix;
else
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0f,
(float)WINDOW_WIDTH / WINDOW_HEIGHT, 1.0f, 30000.0f); //视截体远景设为30000.0f,这样就不怕看不到远处的物体了
//设置投影变换矩阵
m_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}
//-----------------------------------------------------------------------------
//沿右向量平移fUnit个单位
//-----------------------------------------------------------------------------
void CameraClass::MoveAlongRightVec(float fUnits)
{
//直接乘以fUnits的量来累加就好了
m_vCameraPosition += m_vRightVector * fUnits;
m_vTargetPosition += m_vRightVector * fUnits;
}
//-----------------------------------------------------------------------------
//沿上向量平移fUnit个单位
//-----------------------------------------------------------------------------
void CameraClass::MoveAlongUpVec(float fUnits)
{
//直接乘以fUnits的量来累加就好了
m_vCameraPosition += m_vUpVector * fUnits;
m_vTargetPosition += m_vUpVector * fUnits;
}
//-----------------------------------------------------------------------------
//沿观察向量平移fUnit个单位
//-----------------------------------------------------------------------------
void CameraClass::MoveAlongLookVec(float fUnits)
{
//直接乘以fUnits的量来累加就好了
m_vCameraPosition += m_vLookVector * fUnits;
m_vTargetPosition += m_vLookVector * fUnits;
}
//-----------------------------------------------------------------------------
//沿右分量旋转fAngle个弧度单位的角度
//-----------------------------------------------------------------------------
void CameraClass::RotationRightVec(float fAngle)
{
D3DXMATRIX R;
D3DXMatrixRotationAxis(&R, &m_vRightVector, fAngle); //创建出绕m_vRightVector旋转fAngle个单位的R矩阵
D3DXVec3TransformCoord(&m_vUpVector, &m_vUpVector, &R); //让m_vUpVector向量绕m_vRightVector旋转fAngle个角度
D3DXVec3TransformCoord(&m_vLookVector, &m_vLookVector, &R); //让m_vLookVector向量绕m_vRightVector旋转fAngle个角度
float length = D3DXVec3Length(&(m_vTargetPosition - m_vCameraPosition));
m_vTargetPosition = m_vCameraPosition + m_vLookVector * length; //更新一下观察点的新位置
}
//-----------------------------------------------------------------------------
//沿观察分量旋转fAngle个弧度单位的角度
//-----------------------------------------------------------------------------
void CameraClass::RotationLookVec(float fAngle)
{
D3DXMATRIX R;
D3DXMatrixRotationAxis(&R, &m_vLookVector, fAngle); //创建出绕m_vLookVector旋转fAngle个单位的R矩阵
D3DXVec3TransformCoord(&m_vUpVector, &m_vUpVector, &R); //让m_vUpVector向量绕m_vLookVector旋转fAngle个角度
D3DXVec3TransformCoord(&m_vRightVector, &m_vRightVector, &R); //让m_vRightVector向量绕m_vLookVector旋转fAngle个角度
}
//-----------------------------------------------------------------------------
//沿上分量旋转fAngle个弧度单位的角度
//-----------------------------------------------------------------------------
void CameraClass::RotationUpVec(float fAngle)
{
D3DXMATRIX R;
D3DXMatrixRotationY(&R, fAngle); //创建出绕Y轴旋转fAngle个单位的R矩阵
D3DXVec3TransformCoord(&m_vUpVector, &m_vUpVector, &R); //让m_vUpVector向量绕Y轴旋转fAngle个角度
D3DXVec3TransformCoord(&m_vRightVector, &m_vRightVector, &R); //让m_vRightVector向量绕Y轴旋转fAngle个角度
D3DXVec3TransformCoord(&m_vLookVector, &m_vLookVector, &R); //让m_vLookVector向量绕Y轴旋转fAngle个角度
float length = D3DXVec3Length(&(m_vTargetPosition - m_vCameraPosition));
m_vTargetPosition = m_vCameraPosition + m_vLookVector * length; //更新一下观察点的新位置
}
//-----------------------------------------------------------------------------
//析构函数
//-----------------------------------------------------------------------------
CameraClass::~CameraClass(void)
{
}
WinMain.cpp:
#include <d3d9.h>
#include <d3dx9.h>
#include <tchar.h>
#include "DirectInputClass.h"
#include "CameraClass.h"
#define WINDOW_TITLE L"Direct 3D第一人称摄像机"
//定义顶点结构体
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), u(_u), v(_v){}
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
//---------------------------------------【全局变量声明部分】------------------------------------------------
//描述:全局变量的声明
//-----------------------------------------------------------------------------------------------------------
LPDIRECT3DDEVICE9 g_pd3dDevice; //Direct 3D设备对象
D3DXMATRIX g_matWorld; //世界矩阵
DirectInputClass *g_pDInput; //一个DirectInput类的指针
LPD3DXFONT g_pTextAdapterName; //显卡名字的2D文本
LPD3DXFONT g_pTextHelper; //帮助文本的2D文本
LPD3DXFONT g_pTextInfor; //绘制信息的2D文本
LPD3DXFONT g_pTextFPS; //FPS文本的2D文本
wchar_t g_strAdapterName[60]; //包括显卡名字的字符串
wchar_t g_strFPS[50]; //包含帧频率的字符数组
LPD3DXMESH g_pMesh; //网格对象
D3DMATERIAL9 *g_pMaterials; //网格的材质信息
LPDIRECT3DTEXTURE9 *g_pTextures; //网格的纹理信息
DWORD g_dwNumMtrls; //材质的数目
LPD3DXMESH g_Cylinder; //柱子网格对象
D3DMATERIAL9 g_CylinderMaterial; //柱子材质
LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer; //绘制草地的顶点缓存的对象
LPDIRECT3DTEXTURE9 g_pTexture; //绘制草地的纹理对象
CameraClass *g_pCamera; //虚拟摄像机指针
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HRESULT Direct3D_Init(HWND); //在这个函数中继续Direct3D的初始化
HRESULT Objects_Init(HWND); //在这个函数中进行要绘制的物体的资源初始化
void Direct3D_Render(HWND); //在这个函数中进行Direct3D渲染代码的书写
void Direct3D_ClearUp(); //在这个函数中清理COM资源以及其他资源
float Get_FPS();
void Direct3D_Update();
void HelpText_Render(HWND);
//----------------------------------------【WinMain()函数】-------------------------------------------------
//描述:Windows应用程序的入口函数
//-------------------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wndClass = {0};
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = (WNDPROC)WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = (HICON)LoadImage(NULL, L"icon.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = L"3DGameBase";
if(!RegisterClassEx(&wndClass))
return -1;
HWND hWnd = CreateWindow(L"3DGameBase", WINDOW_TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
MoveWindow(hWnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
g_pDInput = new DirectInputClass();
g_pDInput->Init(hWnd, hInstance, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
PlaySound(L"Final Fantasy XIII.wav", NULL, SND_LOOP | SND_ASYNC | SND_FILENAME);
if(FAILED(Direct3D_Init(hWnd)))
MessageBox(hWnd, L"Direct3D 初始化失败!", L"消息窗口", 0);
MSG msg = {0};
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Direct3D_Update();
Direct3D_Render(hWnd);
}
}
UnregisterClass(L"3DGameBase", 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_ClearUp();进行退出前的资源清理
Direct3D_ClearUp();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
//---------------------------------------------【Direct3D_Init()函数】-----------------------------------------
//描述:Direct3D初始化函数,进行Direct3D的初始化
//---------------------------------------------------------------------------------------------------------------
HRESULT Direct3D_Init(HWND hWnd)
{
//---------------------------------------------------------------------------------------------------------------
//【Direct3D初始化步骤一】:创建Direct3D接口对象,以便用该Direct3D对象创建Direct3D设备对象
//---------------------------------------------------------------------------------------------------------------
LPDIRECT3D9 pD3D = NULL; //Direct3D接口对象的创建。
if((pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL) //初始化Direct3D接口对象,并进行DirectX版本协商。
return E_FAIL;
//---------------------------------------------------------------------------------------------------------------
//【Direct3D初始化步骤二】:获取硬件设备信息
//---------------------------------------------------------------------------------------------------------------
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; //不支持硬件顶点运算,采用软件顶点运算
//---------------------------------------------------------------------------------------------------------------
//【Direct3D初始化步骤三】:填充D3DPRESENT_PARAMETERS结构体
//---------------------------------------------------------------------------------------------------------------
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1;
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;
//---------------------------------------------------------------------------------------------------------------
//【Direct3D初始化步骤四】:创建Direct3D设备接口。
//---------------------------------------------------------------------------------------------------------------
if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, vp, &d3dpp, &g_pd3dDevice)))
return E_FAIL;
//获取显卡信息到g_strAdapterName中,并在显卡名称前加上“当前显卡型号:”字符串
//定义一个D3DADAPTER_IDENTIFIER9结构体,用于存储显卡信息
D3DADAPTER_IDENTIFIER9 Adapter;
//调用GetAdapterIdentifier,获取显卡信息
if(FAILED(pD3D->GetAdapterIdentifier(0, 0, &Adapter)))
return E_FAIL;
//显卡名称现在已经在Adapter.Description中了,但是其为char类型,我们要将其转为wchar_t类型
int len = MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, NULL, 0);
//这步操作完成后,g_strAdapterName中就为当前我们的显卡类型名的wchar_t型字符串了
MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, g_strAdapterName, len);
//定义一个临时字符串,且方便了把"当前显卡型号:"字符串引入我们的目的字符串中
wchar_t tempName[50] = L"当前显卡型号:";
//把当前我们的显卡名加到“当前显卡型号:”字符串后面,结果存在TempName中
wcscat_s(tempName, g_strAdapterName);
//把TempName中的结果拷贝到全局变量g_strAdapterName中
wcscpy_s(g_strAdapterName, tempName);
SAFE_RELEASE(pD3D); //LPDIRECT3D9接口对象的使命完成,将其释放掉
if(FAILED(Objects_Init(hWnd))) // 调用一次Objects_Init,进行渲染资源的初始化
return E_FAIL;
return S_OK;
}
//------------------------------------------【Objects_Init()】函数---------------------------------------------
//描述:渲染资源初始化函数,在此函数中进行要被渲染的物体的资源的初始化
//---------------------------------------------------------------------------------------------------------------
HRESULT Objects_Init(HWND hWnd)
{
//创建字体
if(FAILED(D3DXCreateFont(g_pd3dDevice, 36, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, 0, L"Calibri", &g_pTextFPS)))
return E_FAIL;
if(FAILED(D3DXCreateFont(g_pd3dDevice, 20, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, 0, L"华文中宋", &g_pTextAdapterName)))
return E_FAIL;
if(FAILED(D3DXCreateFont(g_pd3dDevice, 23, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, 0, L"微软雅黑", &g_pTextHelper)))
return E_FAIL;
if(FAILED(D3DXCreateFont(g_pd3dDevice, 26, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, 0, L"黑体", &g_pTextInfor)))
return E_FAIL;
//从X文件中加载网格数据
LPD3DXBUFFER pAdjBuffer = NULL;
LPD3DXBUFFER pMtrlBuffer = NULL;
//读取材质和纹理数据
D3DXLoadMeshFromX(L"WYJ.X", D3DXMESH_MANAGED, g_pd3dDevice, &pAdjBuffer, &pMtrlBuffer, 0,
&g_dwNumMtrls, &g_pMesh);
g_pMaterials = new D3DMATERIAL9[g_dwNumMtrls];
g_pTextures = new LPDIRECT3DTEXTURE9[g_dwNumMtrls];
//创建一个D3DXMATERIAL结构体用于读取材质和纹理信息
D3DXMATERIAL *pMtrl = (D3DXMATERIAL *)pMtrlBuffer->GetBufferPointer();
for(DWORD i = 0; i < g_dwNumMtrls; ++i)
{
//获取材质,并设置一下环境光的颜色值
g_pMaterials[i] = pMtrl[i].MatD3D;
g_pMaterials[i].Ambient = g_pMaterials[i].Diffuse;
//创建一下纹理对象
D3DXCreateTextureFromFileA(g_pd3dDevice, pMtrl[i].pTextureFilename, &g_pTextures[i]);
}
SAFE_RELEASE(pAdjBuffer);
SAFE_RELEASE(pMtrlBuffer);
//创建一片草坪 50*50=250张纹理
g_pd3dDevice->CreateVertexBuffer(4*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_MANAGED, &g_pVertexBuffer, 0);
CUSTOMVERTEX *pVertices = NULL;
g_pVertexBuffer->Lock(0, 4*sizeof(CUSTOMVERTEX), (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, NULL);
g_CylinderMaterial.Ambient = D3DXCOLOR(0.9f, 0.0f, 0.8f, 1.0f);
g_CylinderMaterial.Diffuse = D3DXCOLOR(0.9f, 0.0f, 0.8f, 1.0f);
g_CylinderMaterial.Specular = D3DXCOLOR(0.9f, 0.2f, 0.9f, 0.9f);
g_CylinderMaterial.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_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
//创建并初始化虚拟摄像机
g_pCamera = new CameraClass(g_pd3dDevice);
g_pCamera->SetCameraPosition(&D3DXVECTOR3(0.0f, 200.0f, -300.0f)); //设置摄像机所在的位置
g_pCamera->SetTargetPosition(&D3DXVECTOR3(0.0f, 200.0f, 0.0f)); //设置观察点的位置
g_pCamera->SetViewMatrix(); //设置取景变换矩阵
g_pCamera->SetProjMatrix(); //设置投影变换矩阵
//以下这段代码用于限制鼠标光标移动区域
POINT lt, rb;
RECT rect;
GetClientRect(hWnd, &rect);//取得窗口内部矩形
//将矩形左上点坐标存入lt中
lt.x = rect.left;
lt.y = rect.top;
//将矩形右下坐标存入rb中
rb.x = rect.right;
rb.y = rect.bottom;
//将lt和rb的窗口坐标转换为屏幕坐标
ClientToScreen(hWnd, <);
ClientToScreen(hWnd, &rb);
//以屏幕坐标重新设定矩形区域
rect.left = lt.x;
rect.top = lt.y;
rect.right = rb.x;
rect.bottom = rb.y;
//限制鼠标光标移动区域
ClipCursor(&rect);
return S_OK;
}
//-----------------------------------------【Get_FPS()函数】---------------------------------------------------
//描述:用于计算帧频率
//---------------------------------------------------------------------------------------------------------------
float Get_FPS()
{
//定义四个静态变量
static int frameCount = 0; //帧数
static float currentTime = 0; //当前时间
static float lastTime = 0; //上次计算帧频率的时间
static float fps = 0; //需要计算的fps值
++frameCount; //每调用一次此函数,帧数加一
//获取系统时间, timeGetTime() 返回系统时间,以毫秒为单位,乘以0.001得到秒
currentTime = timeGetTime() * 0.001f;
//如果当前时间减去之前计算帧频率的时间大于1秒钟,就进行帧频率的更新,并将帧数归零
if(currentTime - lastTime > 1.0f) //将时间控制在1秒钟
{
fps = frameCount / (currentTime - lastTime); //计算这一秒的fps值
frameCount = 0; //将本次帧数清零
lastTime = currentTime; //将当前时间赋给上次计算帧频率的时间,作为下一秒的基准时间
}
return fps;
}
void HelpText_Render(HWND hWnd)
{
//定义一个矩形,用来获取主窗口矩形
RECT formatRect;
GetClientRect(hWnd, &formatRect);
//在窗口右上角处,显示每秒帧数
swprintf_s(g_strFPS, L"FPS:%.3f", Get_FPS());
g_pTextFPS->DrawText(0, g_strFPS, -1, &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_XRGB(255,255,255));
//显示显卡类型名
g_pTextAdapterName->DrawText(0, 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(0, L" ESC键 : 退出程序", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT,
D3DCOLOR_RGBA(255,200,0,255));
}
void Direct3D_Update()
{
//使用DirectInput类读取数据
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_I))
g_pCamera->MoveAlongUpVec(0.3f);
if(g_pDInput->IsKeyDown(DIK_K))
g_pCamera->MoveAlongUpVec(-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_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);
//计算并设置取景变换矩阵
g_pCamera->SetViewMatrix();
//鼠标滚轮控制观察点收缩操作
static float fPosZ = 0.0f;
fPosZ += g_pDInput->MouseDZ() * 0.03f;
//把正确的世界变换矩阵存到g_matWorld中
D3DXMatrixTranslation(&g_matWorld, 0, 0, fPosZ);
}
//----------------------------------------【Direct3D_Render()函数】--------------------------------------------
//描述:使用Direct3D进行渲染
//---------------------------------------------------------------------------------------------------------------
void Direct3D_Render(HWND hWnd)
{
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤一】:清屏操作
//---------------------------------------------------------------------------------------------------------------
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(50, 100, 250), 1.0f, 0);
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤二】:开始绘制
//---------------------------------------------------------------------------------------------------------------
g_pd3dDevice->BeginScene(); //开始绘制
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤三】:正式绘制
//---------------------------------------------------------------------------------------------------------------
//绘制人物
g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_matWorld); //设置模型的世界矩阵,为绘制做准备
// 用一个for循环,进行模型的网格各个部分的绘制
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 / 2.0f);
g_pd3dDevice->SetMaterial(&g_CylinderMaterial);
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);
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤四】:结束绘制
//---------------------------------------------------------------------------------------------------------------
g_pd3dDevice->EndScene(); //结束绘制
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤五】:显示翻转
//---------------------------------------------------------------------------------------------------------------
g_pd3dDevice->Present(NULL, NULL, NULL, NULL); //翻转与显示
}
//------------------------------------------------【 Direct3D_ClearUp函数】------------------------------------------
//描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
//-------------------------------------------------------------------------------------------------------------------
void Direct3D_ClearUp()
{
//释放COM接口对象
SAFE_RELEASE(g_pd3dDevice);
SAFE_RELEASE(g_pTextFPS);
SAFE_RELEASE(g_pTextHelper);
SAFE_RELEASE(g_pTextAdapterName);
SAFE_RELEASE(g_pTextInfor);
SAFE_DELETE(g_pDInput);
SAFE_RELEASE(g_pMesh);
SAFE_DELETE(g_pMaterials);
for(DWORD i = 0; i < g_dwNumMtrls; ++i)
SAFE_RELEASE(g_pTextures[i]);
SAFE_DELETE(g_pTextures);
SAFE_RELEASE(g_Cylinder);
SAFE_RELEASE(g_pVertexBuffer);
SAFE_RELEASE(g_pTexture);
SAFE_DELETE(g_pCamera);
}