看过Direct3D初始化之后,我们来总体的串一下,搞出个示例,方便学习。
我们现在要做的就是实现Direct3D的初始化,并将背景设置为红色。
在下面的例子中,我们使用了d3dUtility.h/cpp,这两个文件中就是实现每一个Direct3D应用程序都要干的事情,例如创建窗口、初初始化Direct3D、进入应用程序消息循环等,现在我们将这些共性的任务封装在一起。
开始例子之前,我们先来研究一下d3dUtility.h里面的内容:
#pragma once
#include "d3dx9.h"
namespace d3d {
bool InitD3D(
HINSTANCE hInstance, //应用程序实例
int width,
int height, //后台缓存的尺寸
bool windowed, //是全屏还是窗口
D3DDEVTYPE devicetype, //HAL 还是 REF
IDirect3DDevice9** device //创建的设备
);
int EnterMsgLoop(
bool (*ptr_dispaly)(float timeDelta)
);
// 系统的回调函数
// 在创建窗口类信息结构体的时候,里面需要填写的回调函数,
// 可以参见博客:https://blog.csdn.net/DY_1024/article/details/89178348第一小节
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
templatevoid Release(T t)
{
if (t)
{
t->Release();
t = 0;
}
}
templatevoid Delete(T t)
{
if (t)
{
delete t;
t = 0;
}
}
}
里面头文件是我们需要的其他头文件,如果没有下载DirectX9的SDK的话,可以在这个链接下载:
链接:https://pan.baidu.com/s/1028NylXtE6T3Y7MSVEs2GQ 密码:rp5x
下载之后再头文件里面依赖DX9的include头文件(就在Iclude文件夹里面),然后将DX9的SDK里面lib库的路径依赖上(在Lib文件夹里面),然后在库依赖项里面填上:
d3d9.lib
d3dx9.lib
winmm.lib
应该就差不多了~
关于d3dUtility.h的相关内容:
InitD3D:该函数主要对应用程序的主窗口进行了初始化,并执行了之前我们在这篇博客中,所讨论的Direct3D初始化的过程。如果该函数返回成功,就会的到一个指向已经创建好的Direct3DDevice6接口的指针。
EnterMsgLoop:该函数封装了应用程序的消息循环,他接收一个指向显示函数的函数指针。该显示函数就是实现绘制功能的那个函数。该消息循环函数需要知道使用哪个显示函数,然后才进行调用。
然后给出了两个模板,用来释放资源。
d3dUtility.cpp里面就是给出了这两个函数的实现:
#include "d3dUtility.h"
#include
int d3d::EnterMsgLoop(bool(*ptr_display)(float timeDelta))
{
// window消息结构体
MSG msg;
::ZeroMemory(&msg, sizeof(MSG));
//timeGetTime:检索系统时间,系统时间是自Windows启动以来的时间
static float lastTime = (float)timeGetTime();
//WM_QUIT:关闭消息循环
while (msg.message != WM_QUIT)
{
if (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
else
{
//相邻两次调用timeGetTime的时间间隔,就是产生相邻帧时间间隔
float currTime = (float)timeGetTime();
float timeDelta = (currTime - lastTime)*0.001f;
ptr_display(timeDelta);
lastTime = currTime;
}
}
return msg.wParam;
}
bool d3d::InitD3D(
HINSTANCE hInstance,
int width, int height,
bool windowed,
D3DDEVTYPE devicetype,
IDirect3DDevice9** device)
{
// 初始化一个窗口
//一个结构体:包含了一个窗口类的全部信息
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW; //表示窗口的高度或者宽度改变时,是否重画窗口
wc.lpfnWndProc = (WNDPROC)d3d::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "Direct3D9App";
if (!RegisterClass(&wc))
{
::MessageBox(0,"RegisterClass() - FAILED",0,0);
return false;
}
//定义一个窗口句柄
HWND hwnd = 0;
hwnd = ::CreateWindow("Direct3D9App", "Direct3D9App",
WS_EX_TOPMOST,
0, 0, width, height,
0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/);
if (!hwnd)
{
::MessageBox(0, "CreateWindow() - FAILED", 0, 0);
return false;
}
::ShowWindow(hwnd, SW_SHOW);
::UpdateWindow(hwnd);
//初始化D3D设备
//这是一个即将用来存放函数执行结果的变量
//不是返回结果的句柄
HRESULT hr = 0;
//获取一个Direct3D的指针
IDirect3D9* d3d9 = 0;
d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
if (!d3d9)
{
::MessageBox(0, "Direct3DCreate9() - FAILED", 0, 0);
return false;
}
//检查设备是否支持硬件顶点运算
D3DCAPS9 caps;
d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, devicetype, &caps);
int vp = 0;
if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
{
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
else
{
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
//到达这里我们的vp里面就存放着我们的设备到底是不是支持硬件顶点运算
//现在将我们的设备的信息储存在一个结构体中,方便以后的使用
//也就是填充结构体:D3DPRESENT_PARAMETERS
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = windowed;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
//设备支持不支持硬件顶点运算,以及设备的信息我们都知道了
//现在我们就开始创建一个显示的设备
hr = d3d9->CreateDevice(
D3DADAPTER_DEFAULT,
devicetype,
hwnd,
vp,
&d3dpp,
device
);
if (FAILED(hr))
{
//如果失败的话,就将深度缓冲区的位数设置为16位然后再次创建一遍
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
hr = d3d9->CreateDevice(
D3DADAPTER_DEFAULT,
devicetype,
hwnd,
vp,
&d3dpp,
device
);
//如果16位还是失败,就弹窗报错
if (FAILED(hr))
{
d3d9->Release(); // done with d3d9 object
::MessageBox(0, "CreateDevice() - FAILED", 0, 0);
return false;
}
}
d3d9->Release();
return true;
}
然后就是我们的main函数:
main函数里面我们需要实现Setup()、Cleanup()函数,但是现在我们在里面没有内存分配,这两个函数可以直接为空
还需要实现的就是我们在EnterMsgLoop函数里面需要传进去当做参数的显示函数;
main.cpp
#include "d3dUtility.h"
IDirect3DDevice9* Device = 0;
//因为这个例子中没有任何资源的分配,所以Setup函数和Cleanup函数是空函数
bool Setup()
{
return true;
}
void Cleanup()
{}
// 展示函数
bool Display(float timeDelta)
{
if (Device)
{
//
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffff0000, 1.0f, 0);
Device->Present(0, 0, 0, 0); //提交后台缓冲
}
return true;
}
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_KEYDOWN:
//当我们在键盘接收到ESC按下的时候,就退出
if (wParam == VK_ESCAPE)
{
::DestroyWindow(hwnd);
}
break;
}
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hinstance,
HINSTANCE prevInstance,
PSTR cmdLine,
int showCmd)
{
//初始化一个Init3D对象
if (!d3d::InitD3D(hinstance, 640, 480, true, D3DDEVTYPE_HAL, &Device))
{
::MessageBox(0, "InitD3D() - FAILED", 0, 0);
return 0;
}
//分配资源
if (!Setup())
{
::MessageBox(0, "Setup() - FAILED", 0, 0);
return 0;
}
//使用d3d命名空间里面EnterMsgLoop函数
d3d::EnterMsgLoop(Display);
Cleanup();
//设备对象调用函数进行资源回收
Device->Release();
return 0;
}
小节:
1、Direct3D可以被认为是程序员和硬件设备之间的媒介,程序员调用一个Direct3D接口也就是在命令硬件设备和HAL设备进行交互
2、REF设备允许开发人员使用那些Direct3D支持但是硬件设备不支持的功能
3、COM是一项可以使DirectX独立于编程语言,我们不需要知道COM实现的细节,我们只需要知道怎么获取和使用然后释放就可以
4、表面是Direct3D用来储存2D图形数据的专用接口,D3DFORMAT枚举类型的成员指定了表面的像素格式;表面和其他的资源可以被存放在其他几个不同的资内存池中,内存池的类型由D2DPOOL枚举类型的成员决定,此外表面还可以进行多重采样,来创建一副比较平滑的图像
5、IDirect3D接口主要用获取系统图形设备的信息,例如:借助该接口我们可以获取某一设备的特性
6、可以将接口IDirect3DDevice0视为控制图形设备的软件接口。例如:调用IDirect3DDevice9::Clear可命令控制的图形设备对指定的表面进行清除操作