【游戏程序设计】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;			//更新一下观察点的新位置
}

运行结果:

【游戏程序设计】Direct 3D第一人称摄像机

【游戏程序设计】Direct 3D第一人称摄像机

源代码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, &lt);
	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);
}