第二章 DirectX 基础(上)

首先,我们需要下载DirectXSDK包,并安装到本地电脑上。

Download DirectX SDK from Official Microsoft Download Center

Windows 8操作系统开始,DirectX SDK已经集成到Windows SDK了,不需要单独安装!安装Visual Studio的时候勾上Windows SDK即可!也就是说我们刚刚下载的只适用于Windows XP, Windows 7系统。这个DXSDK_Jun10已经包含了9,10,11三个版本的SDK

DirectX SDK的安装目录结构如下:

Documentation里面是帮助文档,可以查询API,但是英文的,不是很方便。

IncludeLib是我们创建Visual Studio工程后需要引入的。

Samples是一些C++案例代码,里面的SampleBrowser就是案例浏览程序。

另外,我们可以去官方查看API的介绍,是中文的,地址如下:

https://docs.microsoft.com/zh-cn/windows/win32/classic-directx-graphics

这个API文档虽然是官方,但是对API的详细说明案例却很少。

接下来我们使用VS2019创建一个DirectX基础程序,首先创建C++空项目。

项目名称为“D3D_02_DirectX”,并设置存放路径,然后点击创建。然后我们创建“main.cpp”源码文件,并将上一章中的Windows应用程序的代码全部复制过来。同时,不要忘了在“链接器”下的“系统”项目中,将“子系统”的值由“控制台”改为“窗口”。接下来,我们仍然在项目的属性面板中,添加DirectX的支持。也就是添加“包含目录”和“库目录”。使用C++语言开发DirectX游戏时,必须把DirectXSDK引入到项目中来。如下图:

这个操作过程比较复杂,见下图:

 

 

然后点击“确定”就完成设置了。两个路径都是同样的操作流程。这里假设我们将DirectX安装到了E:\Microsoft DirectX SDK (June 2010)目录下。

VC++目录 –> 包含目录:E:\Microsoft DirectX SDK (June 2010)\Include

VC++目录 –> 库目录:  E:\Microsoft DirectX SDK (June 2010)\Lib\x86

在项目代码(main.cpp)中引入DirectX的代码如下:

// 引入头文件
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>

// 引入依赖的库文件
#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")

#define WINDOW_LEFT		200				// 窗口位置
#define WINDOW_TOP		100				// 窗口位置
#define WINDOW_WIDTH	800				// 窗口宽度
#define WINDOW_HEIGHT	600				// 窗口高度
#define WINDOW_TITLE	L"D3D游戏开发"	// 窗口标题
#define CLASS_NAME		L"D3D游戏开发"	// 窗口类名

首先我们需要创建一个具有主窗口的应用程序,并且在显示窗口之前初始化Direct3D,然后在消息循环中不断对3D场景进行绘制。其中消息循环和Direct3D的绘制过程是不断循环进行的。如果程序中有消息需要处理的话,优先处理消息,再进行Direct3D绘制。如果没有消息处理的话,就直接进行Direct3D绘制。游戏的开发本质就是一个不断循环绘制场景的过程,绘制场景过程中会根据用户的输入事件,做相应的处理,Direct3D会根据这个处理的结果进行绘制。其实这个过程在我们之前使用C++创建拼字游戏的时候,就详细的介绍过。两者区别在于一个是控制台程序,一个是Windows窗体程序。但是游戏开发的逻辑本质是一样的,Windows窗体程序在绘制2D图像或3D模型的时候,需要使用“Direct3D设备指针对象”,这个东西本质上就是我们的显卡,因为计算机的图像绘制都是由显卡来完成的。越是逼真的3D画面,就越需要高性能的显卡来支持。那么,本章就先介绍如何初始化“Direct3D设备指针对象”。

首先,我们先声明全局的“Direct3D设备指针对象”。

// Direct3D设备指针对象
LPDIRECT3DDEVICE9 D3DDevice = NULL;

接下来还是先声明几个函数,分别用于不用的逻辑操作。

// 声明游戏开始函数(初始化DirectX)
bool startGame(HWND hwnd, HINSTANCE hInstance);

// 声明游戏结束函数(释放对象)
void endGame();

// 声明游戏循环中处理用户输入函数
void update(int type, WPARAM wParam);

// 声明游戏循环中游戏界面渲染函数
void render(HWND hwnd);

// 声明窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

接下来,我就可以将上一章节中wWinMain函数和WndProc函数复制过来。复制过来后,我们可以运行一下,保证代码没有任何问题。目前我们重点介绍startGame函数,它的主要目的就是创建Direct3D设备指针对象,也就是我们上面声明的LPDIRECT3DDEVICE9 D3DDevice。创建该对象共计5个步骤,代码如下:

// 第一步:创建Direct3D接口对象
LPDIRECT3D9 D3D9 = NULL;
if (NULL == (D3D9 = Direct3DCreate9(D3D_SDK_VERSION))) return false;

// 第二步:获取硬件设备(显卡)信息,主要参数为是否支持硬件顶点运算
int vp = 0;
D3DCAPS9 deviceCaps;
if (FAILED(D3D9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &deviceCaps))) return false;
if (deviceCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
	vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;	// 支持硬件顶点运算
else
	vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;	// 不支持硬件顶点运算

// 第三步:构造D3DPRESENT_PARAMETERS结构体
D3DPRESENT_PARAMETERS D3DParameters;
ZeroMemory(&D3DParameters, sizeof(D3DParameters));
D3DParameters.BackBufferWidth = WINDOW_WIDTH;		// 指定后台缓冲区的宽度,窗口的宽度
D3DParameters.BackBufferHeight = WINDOW_HEIGHT;		// 指定后台缓冲区的高度,窗口的高度
D3DParameters.BackBufferFormat = D3DFMT_UNKNOWN;	// 指定后台缓冲区的像素格式
D3DParameters.BackBufferCount = 1;					// 指定后台缓冲区的数量
D3DParameters.MultiSampleType = D3DMULTISAMPLE_NONE;// 指定多重采样的类型
D3DParameters.MultiSampleQuality = 0;				// 指定多重采样的格式
D3DParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;	// 指定Direct3D如何将后台缓冲区内容复制到前台的缓存中
D3DParameters.hDeviceWindow = hwnd;					// 指定当前窗口句柄
D3DParameters.Windowed = true;						// 指定窗口模式
D3DParameters.EnableAutoDepthStencil = true;		// 是否开启深度缓存和模板缓存。
D3DParameters.AutoDepthStencilFormat = D3DFMT_D24S8;// 指定深度缓冲及模板缓冲区的格式
D3DParameters.Flags = 0;							// 表示锁定后备缓冲区
D3DParameters.FullScreen_RefreshRateInHz = 0;		// 指定屏幕刷新率
D3DParameters.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;	// 指定后备缓冲区与前台缓冲区的交换频率

// 第四步:创建Direct3D设备指针对象
if (FAILED(D3D9->CreateDevice(
	D3DADAPTER_DEFAULT,			// 指定将要使用的显卡适配器的标识号
	D3DDEVTYPE_HAL,				// 指定设备的类型
	NULL,						// 指定焦点窗口
	vp,							// 表示设备行为标示
	&D3DParameters,				// 指定到一个D3DPRESENT_PARAMETERS的结构体
	&D3DDevice)))				// 返回Direct3D设备指针对象
	return false;

// 第五步:释放Direct3D接口对象
D3D9->Release();
D3D9 = NULL;
return true;

以上就是startGame函数的全部内容至此Direct3D的初始化工作完毕了!至于里面的很多配置参数,我们没有必要深入的了解,这些并不是我们现在这个阶段需要深入了解的内容。既然我们已经完成了的startGame函数,那么该函数在哪里调用呢。一般情况下,我们都是在窗口显示之前的时候,就把DirectX初始化完毕。也就是说,我们在wWinMain函数中创建Windows窗体的第三步和第四步中间位置来调用startGame函数。

// 调用游戏开始函数(初始化DirectX)
if (false == startGame(hwnd, hInstance)) return -1;

初始化完“Direct3D设备指针对象”后,我们就可以用它来进行绘制了。在Direct3D中,使用了一种称为交换链(Page Flipping)的技术,来让画面能够平滑的过渡。交换链由两个或者两个以上的表面组成,而每个表面都是存储着2D图形的一个线性数组,其中每个元素都表示屏幕上的一个像素。对于三维物体,Direct3D使用深度缓冲区为最终绘制的图像的每个像素都存储一个深度信息(就是三维物体的Z轴信息)。前台缓冲区和后台缓冲区是位于系统内存或显存的内存块,对应于将要显示的二维显示区域。前台缓冲区是显示在屏幕上的,而后台缓冲区则是图形绘制。我们的图像在经过后台缓冲区的内容准备好之后,就可以和前台缓冲区进行一个交换操作。这就是交换链页面翻转。通过前台缓冲区和后台缓冲区的配合,就可以流畅而高效的绘制出图像来。我们在初始化Direct3D的时候,构D3DPRESENT_PARAMETERS的结构体中就有一项参数可以设置后台缓冲区的数量。Direct3D可以调用Present()方法来进行页面的翻转和显示,该方法其实就是执行图像渲染的工作,因此放置在我们自定义的render方法中,代码如下:

// 第一步:清屏操作
D3DDevice->Clear(
	0,							// 指定清除矩形的数量,一般设置0即可
	NULL,						// 指定清除矩形,设置为NULL表示视口所有矩形都要清除
	D3DCLEAR_STENCIL | D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, // 指定清除的缓冲区,包括模板/颜色/深度三种缓冲区,用|连接
	D3DCOLOR_XRGB(255, 255, 255),// 指定清除颜色缓冲区的颜色值
	1.0f,						// 指定清除深度缓冲区后的深度值,此参数取值从0.0到 1.0
	0);							// 指定清除模板缓冲区后的模板值,取默认值0即可

// 第二步:开始绘制
D3DDevice->BeginScene();

// 第三步:绘制内容

// 第四步:结束绘制
D3DDevice->EndScene();

// 第五步:显示翻转
D3DDevice->Present(
	NULL,		// 表示后台缓冲区,NULL值是全部的后台缓存矩形表面
	NULL,		// 表示前台缓冲区,NULL值指定整个前台缓存表面
	NULL,		// 表示窗口的句柄,NULL表示当前窗口
	NULL);		// 指定需要拷贝的后台缓存表面的大小和数据,一般是NULL

这个render函数在消息循环中被调用,这样在我们程序中,每秒可能很多次的页面翻转来绘制图像。因此我们仍然需要调整一下

// 获取消息并交给窗口过程函数
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
} else {
	// 渲染游戏画面
	render(hwnd);
}

上述代码的大致意思就是说,如果有事件发生,就处理事件,否则就绘制游戏画面。我们之前讲过,游戏渲染就是一个无限循环的过程,因此在消息循环中调用就再合适不过了。另外,还有一个位置需要调用render函数,就是窗体重绘消息事件中。如下:

case WM_PAINT:
	render(hwnd);				// 调用游戏界面渲染函数
	ValidateRect(hwnd, NULL);	// 窗口重绘消息
	break;

为了检测我们的代码运行效果,这里我们并不打算绘制复杂的图形,而是打印一些文字即可。使用DirectX打印文字,我们需要先创建一个文字对象。

// Direct3D设备指针对象
LPDIRECT3DDEVICE9 D3DDevice = NULL;

// 字体指针对象
LPD3DXFONT D3DFont = NULL;

我们还是先声明一个全局字体对象,就放在Direct3D设备指针对象下面即可。声明完之后,我们需要创建,这个代码就可以和创建Direct3D设备指针对象放在一起就行了。

// 第五步:释放Direct3D接口对象
D3D9->Release();
D3D9 = NULL;

// 创建一个字体对象
D3DXCreateFont(D3DDevice, 24, 0, 1, 1, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"微软雅黑", &D3DFont);
return true;

创建字体对象的参数,我们暂时不用了解太多。创建完之后,我们就可以使用它来输出文字了。也就是在 render 函数的第三步位置。

// 第三步:绘制文字
RECT rect;
GetClientRect(hwnd, &rect);
D3DFont->DrawText(0, L"Hello,Direct3D游戏开发!", -1, &rect, DT_CENTER, D3DCOLOR_XRGB(255, 0, 0));

运行我们的程序,效果如下:

本课程的所有代码案例下载地址:

workspace.zip

备注:这是我们游戏开发系列教程的第二个课程,这个课程主要使用C++语言和DirectX来讲解游戏开发中的一些基础理论知识。学习目标主要依理解理论知识为主,附带的C++代码能够看懂且运行成功即可,不要求我们使用DirectX来开发游戏。课程中如果有一些错误的地方,请大家留言指正,感激不尽!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咆哮的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值