游戏编程之DirectX的修炼:三(DirectX的初始化:下)

来源:互联网 发布:阿里云 80端口 编辑:程序博客网 时间:2024/05/18 02:50

        哇卡卡卡,哈哈哈,这节咋们就来学习初始化DirectX的具体代码和细节吧。

        还几次上节说的我们的初始化主要有两个步骤,我们再看下图。


       是否心里已经明白我们大概要做什么了那?

      1. 好,首先来解决初始化的第一步,获得IDirect3DDevice9这个接口。 

       IDirect3DDevice9这个接口又叫设备接口,设备,什么设备?当然就是我们的硬件了。回想之前我们创建窗口的时候,我们说要有一个自己的窗口,需要一些最基本的信息来告诉我们窗口长啥样子,同理,我们在创建IDirect3DDevice9这个设备接口时候我们也要知道一些基本的信息,才能让我们知道这个设备接口大概是什么样子(这里的样子指的是它的属性,并不是具体外观),而这些基本信息就是我们的硬件信息了,因为我们说过DirectX是可以直接和硬件打交道的对吧。那么,也就是说。创建一个设备接口,我们需要获取硬件信息,然后把这些消息告知我们的设备接口,我们的设备接口就算完成了。

       总结起来就是两步:

      1.1:获取硬件信息

      1.2:将信息告知我们的设备接口,创建设备接口

没问题把,获得设备接口就这么两步,那我们先走第一步:获得硬件信息

          首先,想要获取,硬件信息,我们需要创建一个LPDIRECT3D9接口,创建这个接口不需要任何条件,我们通过这个接口调用GetDeviceCaps这个函数,就能获得硬件信息,同时我们也是通过LPDIRECT3D9接口调用CreateDevice函数最终创建我们的IDirect3DDevice9设备接口的。稍后便会讲到。我们先看一下用LPDIRECT3D9获得硬件信息的代码。

        LPDIRECT3D9 pD3D = Direct3DCreate9(D3D_SDK_VERSION);//创建接口对象获取硬件信息并为后面的设备对象做准备if (NULL == pD3D) { 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;//硬件顶点运算模式elsevp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;//软件顶点运算模式

      我们看一下这一段代码,首先第一句话,我们使用了一个Direct3DCreate9函数来创建了一个LPDIRECT3D9接口,Direct3DCreate9这个函数只有一个参数,这个参数的意义是我们当前的DirectX版本,保证我们包含的头文件能正确调用相应的.dll库。如果失败,说明版本协商有问题。

      然后是我们定义了一个D3DCAPS9型的结构体来保存我们的硬件信息。大家看到了我们还定义了一个整形的vp变量,这个变量是用来保存我们硬件是否支持硬件顶点运算模式,如果支持,那么后面我们将告诉我们的设备接口,让它在渲染的时候使用硬件顶点运算模式,如果不行就只能使用软件顶点运算模式了。最后的那句if else语句就是用来判断是否支持硬件顶点运算模式,当然,硬件顶点运算模式是要比软件顶点运算模式快很多的。这也是我们判断的原因。

      最后就是中间的GetDeviceCaps函数了,这个函数就是用来获取硬件信息的。

HRESULT GetDeviceCaps(  [in]  UINT       Adapter,//显卡序号  [in]  D3DDEVTYPE DeviceType,//设备类型  [out] D3DCAPS9   *pCaps//保存硬件信息的结构体);

第一个参数一般填写默认的:D3DADAPTER_DEFAULT

第二个参数,我们也一般填写硬件设备类型:D3DDEVTYPE_HAL

最后一个参数就是我们需要保存信息的结构体,当我们调用完这个函数以后,设备信息就存到caps里面去了。ok,取得硬件信息就是这么多了。

  1.2:将信息告知我们的设备接口,创建设备接口

我们已经取得了,硬件信息,是不是马上可以开始创建设备接口了哪?是的,但是在创建设备接口之前,我们还需要设置设备接口的另一些详细信息。而幸运的是,这些信息都包含在一个结构体内,还记得我们填窗口填写的WINDCLASSEX吗?这次我们要为设备填写一个叫做D3DPRESENT_PARAMETERS的结构体,这个结构体非常重要!

typedef struct _D3DPRESENT_PARAMETERS_{    UINT                BackBufferWidth;//交换链后台缓存宽度    UINT                BackBufferHeight;//交换链后台缓存高度    D3DFORMAT           BackBufferFormat;//后台缓存格式,他的参数是D3DFORMAT的成员之一    UINT                BackBufferCount;//后台缓存个数    D3DMULTISAMPLE_TYPE MultiSampleType;//多重采样格式    DWORD               MultiSampleQuality;//多重采样类型    D3DSWAPEFFECT       SwapEffect;//将后台缓存内容复制到前台的方式.    HWND                hDeviceWindow;//我们的窗口句柄    BOOL                Windowed;//是否全屏    BOOL                EnableAutoDepthStencil;//自动深度缓存,如果为真,需要设置下一个参数。    D3DFORMAT           AutoDepthStencilFormat;//深度缓存的像素格式    DWORD               Flags;//附加属性    /* FullScreen_RefreshRateInHz must be zero for Windowed mode */    UINT                FullScreen_RefreshRateInHz;//全屏模式时候的刷新率    UINT                PresentationInterval;前台缓存与后台缓存的最大交换速率。在D3DPRESENT中取值} 
好了这就是这个可以为我们的设备接口做出巨大贡献的结构体,可能大家第一次看到会觉得很生疏,如果大家有看不懂的地方,请不要在意,因为你不懂的,后面都会慢慢讲的。大家只需要记住这是个重要的结构体就行。接下来看看它的赋值


        D3DPRESENT_PARAMETERS d3dpp;//一个非常重要的结构体ZeroMemory(&d3dpp, sizeof(d3dpp));//用零填充d3dpp这块内存d3dpp.BackBufferWidth = WINDOW_WEIGHT;//缓冲区宽度d3dpp.BackBufferHeight = WINDOW_HIGHT;//缓冲区高度d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;//缓冲区保存像素格式D3DFORMAT//++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;//深度缓存像素格式D3DFORMAT中取值d3dpp.Flags = 0;//附加属性,通常为0d3dpp.FullScreen_RefreshRateInHz = 0;//全屏模式时候的屏幕刷新率d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;//前后台交换的最大频率可以在D3DPRESENT取值

好了 硬件信息有了,结构体也有啦,那就开始创建我们的设备接口把

pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,hwnd, vp, &d3dpp, &g_pd3dDevice)pD3D->Release();

好吧 创建是非常简单的,一个CreateDevice函数就搞定了,调用这个函数后我们就可以用设备接口IDirect3DDevice9搞事情了。不过在此之前我们还是介绍下CreateDevice

HRESULT CreateDevice(  [in]          UINT                  Adapter,//显卡序号,一般填0,D3DADAPTER_DEFAULT=0  [in]          D3DDEVTYPE            DeviceType,//设备类型,当然是硬件  [in]          HWND                  hFocusWindow,//窗口句柄  [in]          DWORD                 BehaviorFlags,//顶点运算模式  [in, out]     D3DPRESENT_PARAMETERS *pPresentationParameters,//千辛万苦填的接口体  [out, retval] IDirect3DDevice9      **ppReturnedDeviceInterface//指向我们的设备接口的指针,这个参数就是我们的设备接口.);

好啦  到此为止我们Direct3D的初始化第一部分创建设备接口IDirect3DDevice9就讲完了,我们把完整代码贴出来

LPDIRECT3D9 pD3D = Direct3DCreate9(D3D_SDK_VERSION);//创建接口对象获取硬件信息并为后面的设备对象做准备if (NULL == pD3D) { 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;//硬件顶点运算模式elsevp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;//软件顶点运算模式D3DPRESENT_PARAMETERS d3dpp;//一个非常重要的结构体ZeroMemory(&d3dpp, sizeof(d3dpp));//用零填充d3dpp这块内存d3dpp.BackBufferWidth = WINDOW_WEIGHT;//后台缓冲区宽度d3dpp.BackBufferHeight = WINDOW_HIGHT;//后台缓冲区高度d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;//缓冲区保存像素格式D3DFORMATd3dpp.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;//深度缓存像素格式D3DFORMAT中取值d3dpp.Flags = 0;//附加属性,通常为0d3dpp.FullScreen_RefreshRateInHz = 0;//全屏模式时候的屏幕刷新率,窗口时为0在AdapterModes枚举中取值d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;//前后台交换的最大频率可以在D3DPRESENT取值if (FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,hwnd, vp, &d3dpp, &g_pd3dDevice)))return E_FAIL;pD3D->Release();//LPDIRECT3D9接口对象的使命完成释放return S_OK;

以上就是初始化第一部分的全部代码了,记得当我们使用完LPDIRECT3D9这个接口时,要把他Release掉。


       2.使用我们的设备接口进行渲染并且在主事件循环中不断渲染

由于现在我们只是在初始化D3D,所以我们的不会在平面上渲染任何东西,所以这部分的代码也非常简单,就是使用我们的IDirect3DDevice9调用几个函数就可以搞定,然后把他们放入我们的主事件循环中不断地渲染就好啦,那我们来看看这几个函数把。

void Render(){g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0, 0);//清空后台缓存g_pd3dDevice->BeginScene();//开始绘制g_pd3dDevice->EndScene();//结束绘制g_pd3dDevice->Present(NULL, NULL, NULL, NULL);//交换与显示}


好,看上去是不是很简单。我定义了一个函数Render将我们进行渲染地几个函数放在了一起。

首先还是来看看咋们是第一个函数Clear,这个函数的主要作用就是设置我们清空了后台缓冲之后的一些属性。

HRESULT Clear(  [in]       DWORD    Count,//指向一个矩形数组地数量,与下一个函数有关,如果下一个函数为NULL,则这里为零,反之,下一个参数不是NULL,这里就不能写零  [in] const D3DRECT  *pRects,//指向一个矩形结构数组地指针  [in]       DWORD    Flags,//需要清空地缓冲区,它可以是模板缓冲,颜色缓冲,和深度缓冲三个值地任意组合,用|连接。  [in]       D3DCOLOR Color,//清空缓冲区后每个像素地颜色值  [in]       float    Z,//清空深度缓冲后地每个像素地深度值  [in]       DWORD    Stencil//清空模板缓冲后地每个像素地模板值);

好后面的三个函数都非常简单

     BeginScene代表着开始绘制

     EndScene代表着结束绘制

     而我们以后需要在屏幕上绘制东西时的代码函数都在BeginScene和EndScene之间

     当所有的绘制完成之后我们就调用Present将绘制的内容显示出来,这个函数也非常简单四个参数都填NULL就ok了!


既然渲染的函数都写好了,那就赶紧将我们的渲染函数放到主循环函数中开始渲染把。是的没错,但是在那之前我们还有最后一个问题需要处理,什么问题?就是关于我们主事件循环的问题,我们先贴一下主事件循环的代码。

MSG msg = {0};//定义megwhile(GetMessage(&msg, NULL, 0, 0)) //使用getmessage获得消息{TranslateMessage(&msg);//转换消息DispatchMessage(&msg);//发消息给程序,然后交给os调用窗口处理函数}
         之前我们说过,当我们生成窗口之后,OS就会给我们的程序生成一个消息队列,而我们的GetMessage函数则通过While循环不断获取消息,但是大家有没有想过,一旦消息队列为空咋办?会咋办哪?其实,一旦消息队列为空,则GetMessage 函数会一直停在那里等待消息,而不会继续执行下面的代码,试想一下,假如你在玩儿一个游戏,一旦你停止按键盘或者鼠标,整个游戏画面就不再更新,这将是多么可怕的一件事情,所以。我们希望不论消息队列中有没有消息,我们的主循环函数都不会阻塞,并且不断地渲染我们的画面。所以这时候我们需要请出PeekMessage这个函数来代替GetMessage函数。
        那么PeekMessage有什么优点哪?
它唯一的优点就是,当我们的消息队列中没有消息的时候,它会返回一个值继续往下执行,不会一直等待消息队列中有消息才返回。下面我们看看将主事件循环改为PeekMessage之后时什么样

         

MSG msg = { 0 };//定义并初始化 megwhile (msg.message != WM_QUIT)//消息循环{if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) //使用peekmessage获得消息{TranslateMessage(&msg);//转换消息DispatchMessage(&msg);//分发消息给程序,然后交给os调用窗口处理函数}else{Render();}}
好了,我们可以看到由于PeekMessage不会一直等待消息队列的消息,所以当消息队列没有消息的时候,我们就让它执行我们的渲染函数,这样我们的画面就会一直更新,还有一点就是PeekMessage的前四个参数和Get Message一样,只有最后多一个参数,这个参数是告诉PeekMessage在查看消息队列中的消息时,是否要将查看的消息移出消息队列。好吧,最后我们把一个完整的Direct3D初始化程序贴出来吧!

//======================================================================================================////————————————————————————程序说明———————————————————————////程序名称:DirectInitDemo//2017.9.5  淡一抹夕霞//======================================================================================================////==========================================////——————头文件部分——————————//#include"windows.h"#include<d3d9.h>#include<d3dx9.h>//==========================================////==========================================////——————库文件部分——————————//#pragma comment(lib,"d3dx9.lib")#pragma comment(lib,"d3d9.lib")  //==========================================////==========================================////———--———全局变量——————————//LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;//定义一个direct3D设备对象int Color=0;//用于更改清空后台缓存时的颜色//==========================================////==========================================////——————全局函数声明———————--—//LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);//窗口处理函数HRESULT D3Dinit(HWND hWnd, HINSTANCE hInstance);//d3d初始化函数void    Render();//d3d渲染函数//==========================================////===========================================================================================////—————--------------------—程序的主函数WinMain———-----------------------------——-////===========================================================================================//int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){                   ///////////////////////////////////////                   //                                   //                   //-----------设计窗口部分------------//                   //                                   //                   ///////////////////////////////////////WNDCLASSEX wndclassex = { 0 };//创建一个窗口类,并且记得初始化wndclassex.cbSize = sizeof(wndclassex);//节数大小wndclassex.style = CS_VREDRAW | CS_HREDRAW;//样式标记wndclassex.lpfnWndProc = WndProc;//指向窗口事件处理函数的指针wndclassex.hInstance = hInstance;//应用程序实例句柄wndclassex.cbClsExtra = 0;//额外的类信息wndclassex.cbWndExtra = 0;//额外的窗口信息wndclassex.hIcon = LoadIcon(NULL, IDI_APPLICATION);//加载ico图标wndclassex.hCursor = ::LoadCursor(NULL, IDC_ARROW);//指定窗口类的光标句柄wndclassex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);//为成员指定一个灰色画刷wndclassex.lpszMenuName = NULL;//要加入窗口的菜单名wndclassex.lpszClassName = L"WINDOW_CLASS";//窗口类名/////////////////////////////////////////                                   ////-----------注册窗口部分------------////                                   /////////////////////////////////////////RegisterClassEx(&wndclassex);//向我们的"政府申请注册"/////////////////////////////////////////                                   ////-----------创建窗口部分------------//        //                                   //        ///////////////////////////////////////HWND hWnd = CreateWindowEx(NULL, L"WINDOW_CLASS",L"MyDirectDemo",//直接调用创建函数就好了WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,800, 600, NULL, NULL, hInstance, NULL);////////////////////////////////////////////////////////////////////////////////ShowWindow(hWnd, nCmdShow);//显示窗口UpdateWindow(hWnd);//更新窗口    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////在此初始化Direct3DD3Dinit(hWnd, hInstance);////////////////////////////////////////////////////////////////////////////////          ///////////////////////////////////////          //                                   //          //------------主循环部分-------------//          //                                   //          ///////////////////////////////////////MSG msg = { 0 };//定义并初始化 megwhile (msg.message != WM_QUIT)//消息循环{if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) //使用peekmessage或者getmessage获得消息{TranslateMessage(&msg);//转换消息DispatchMessage(&msg);//分发消息给程序,然后交给os调用窗口处理函数}else{Render();}}return msg.wParam;}///////////////////////////////////////////////////                                             ////------------窗口消息处理函数部分-------------////                                             ///////////////////////////////////////////////////LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){PAINTSTRUCT paintStruct;//定义一个paintstruct记录一些绘制信息HDC hdc;//设备环境句柄switch (message)//开始处理{case WM_PAINT://重绘消息  更新客户区hdc = BeginPaint(hWnd, &paintStruct);//指定窗口进行绘图准备,并在ps结构中保存相关信息EndPaint(hWnd, &paintStruct);//窗口绘图过程结束break;case WM_KEYDOWN://键盘按下消息if (wParam == VK_ESCAPE)//如果是escDestroyWindow(hWnd);//销毁窗口,发送WM_DESTROY消息break;case WM_DESTROY://销毁消息PostQuitMessage(0);//向os申请终止请求。break;default:return DefWindowProc(hWnd, message, wParam, lParam);//默认的窗口过程处理函数}return 0;}HRESULT D3Dinit(HWND hWnd, HINSTANCE hInstance){LPDIRECT3D9 pD3D = Direct3DCreate9(D3D_SDK_VERSION);//创建接口对象获取硬件信息并为后面的设备对象做准备if (NULL == pD3D) { 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;//硬件顶点运算模式elsevp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;//软件顶点运算模式D3DPRESENT_PARAMETERS d3dpp;//一个非常重要的结构体ZeroMemory(&d3dpp, sizeof(d3dpp));//用零填充d3dpp这块内存d3dpp.BackBufferWidth = 800;//缓冲区宽度d3dpp.BackBufferHeight =600;//缓冲区高度d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;//缓冲区保存像素格式D3DFORMAT//+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;//附加属性,通常为0d3dpp.FullScreen_RefreshRateInHz = 0;//全屏模式时候的屏幕刷新率,d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;//前后台交换的最大频率if (FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,hWnd, vp, &d3dpp, &g_pd3dDevice)))return E_FAIL;pD3D->Release();//LPDIRECT3D9接口对象的使命完成  释放return S_OK;}void Render(){g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(Color, 0, 255), 1.0, 0);g_pd3dDevice->BeginScene();g_pd3dDevice->EndScene();g_pd3dDevice->Present(NULL, NULL, NULL, NULL);Color += 1;if (Color >= 255)Color = 0;}


好的,大家可以复制代码运行一下看看,是不是觉得很闪?因为我设置了个变量每次清空后台缓存时都将颜色的R值加1;


好吧,这一节就到这里,如果有不明白的,相信大家都明白,大家心中对渲染或许还有疑问,但是没关系,后面会详细介绍。当然有什么建议可以留言哦。感谢!

阅读全文
1 0
原创粉丝点击