• DX9入门笔记1-D3D初始化


    对3D编程期待已久,却一直叶公好龙浅尝辄止。近期在公司实习却无具体的工作安排,琢磨着学习个新的手艺,就又想起了3D Programming。这次从大名鼎鼎的龙书(Introduction to 3D Game Progamming with Directx 9.0)开始学起,坚持...
    作为入门的第一篇笔记,本文对D3D初始化过程做了总结。DirectX3D的初始化需要做两部分工作:创建一个窗口、创建IDirect3DDevice9对象,窗口用于展示D3D渲染出来的场景;IDirect3DDevice9则是一个C++对象,表示用来displaying3D图形的物理设备。本文也主要包含两个部分:

    1. D3D的初始化
    2. 龙书中使用简单框架的代码分析

    1.D3D的初始化

    龙书中将一个D3D的初始化过程分为4个步骤:

    1. 获取IDirect3D9接口的指针,该接口通常用于取得物理设备信息和创建IDirect3DDevice9对象。
    2. 检测设备的处理能力(D3DCAPS9),检测显示适配器(display adapter)是否支持顶点的硬件处理。
    3. 初始化结构体D3DPRESENT_PARAMETERS,该结构指定了一些参数用来创建IDirect3DDevice9。
    4. 创建IDeirect3DDevice9对象。

    1.1取得IDirect3D9指针

    D3D中有专门的函数来获取IDirect3D9指针

    	IDirect3D9* d3d9 = NULL;
    	d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
    

    D3D_SDK_VERSION是创建IDirect3D9唯一需要的参数,该参数能够保证程序是基于当前版本的DirectX生成的。

    1.2 检测硬件的处理能力

    在创建IDirect3DDevice9时需要指定其顶点处理类型(硬件或者软件),硬件顶点处理速度较快,但是不是所有的显示适配器都具有该能力。可以调用函数GetDeviceCaps,该函数的原型如下

    HRESULT IDirect3D9::GetDeviceCaps(
        UINT Adapter,
        D3DDEVTYPE DeviceType,
        D3DCAPS9 *pCaps
    );
    
    1. Adpater 指定要检测的显示适配器
    2. DeviceType 硬件类型,D3DDEVTYPE_HAL(硬件设备)、D3DDEVTYPE_REF(软件设备)。
    3. pCaps 返回D3DCAPS9结构,包含了硬件的所支持的各种特性。
    //  检查硬件的顶点处理能力
    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;
    

    1.3 初始化 D3DPRESENT_PARAMETERS

    D3DPRESENT_PARAMETERS用于指定IDirect3Device9的一些特性,其声明如下:

    typedef struct _D3DPRESENT_PARAMETERS_
    {
        UINT                BackBufferWidth;
        UINT                BackBufferHeight;
        D3DFORMAT           BackBufferFormat;
        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_PARAMETERS;
    
    • BackBufferWidth,BackBufferHeight bakck buffer的宽和高
    • BackBufferFormat back buffer的像素格式,例如:D3DFMT_A8R8G8B8
    • BackBufferCount back buffer的数量,通常为1
    • MultiSampleType,MultiSampleQuality 抗锯齿的类型和质量
    • SwapEffect 指定buffer在交换链(flipping chain)是如何被交换的,改值是D3DSWAPEFFECT类型的枚举,其中D3DSWAPEFFECT_DISCARD是效率 最高的。
    • hDeviceWindow 用于绘制的窗口句柄
    • Windowed 是窗口模式还是全屏,true为全屏。
    • EnableAutoDepthStencil 设置为true,D3D将自动的创建深度/模板buffer
    • AutoDepthStencilFormat 深度/模板buffer的格式
    • Flags 附加的一些特性,可以指定为0或者D3DPRESENTFLAG中某个值。比较推荐下面两个值
      • D3DPRESENTFLAG_LOCKABLE_BACKBUFFER back buffer能给被锁定,这会降低程序的性能。
      • D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL 在present下一个buffer时,当前的深度/模板buffer会被“丢弃(discard)",这有利于提升程序的性能。”discard“的意思是深度/模板buffer所占用的存储空间会被丢弃或者不可用。
    • FullScreen_RefreshRateInHz 刷新频率,使用D3DPRESENT_RATE_DEFAULT指定为默认的刷新频率。
    • PresentationInterval D3DPRESENT成员,两个常用的标志
      • D3DPRESENT_INTERVAL_IMMEDIATE 立即呈现
      • D3DPRESENT_INTERVAL_DEFAULT D3D选中呈现的速度,通常等于刷新率。
        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;
    

    1.4 创建IDirect3Device9

    填充D3DPRESENT_PARAMETERS后,IDirect3Device9就很简单了

    d3d9->CreateDevice(D3DADAPTER_DEFAULT,deviceType,hwnd,vp,&d3dpp,device);
    

    2. 简单框架 d3dUtility.h/cpp

    在龙书的第二章提供了一个简单框架,用于D3D的初始化工作。
    d3dUtility.h如下:

    namespace d3d
    {
    	bool InitD3D(HINSTANCE hInstance, //应用程序实例
    		int width,int height, //Back buffer的size
    		bool windowed, //是否全屏
    		D3DDEVTYPE deviceType, //设备类型 HAL或者REF
    		IDirect3DDevice9** device); // 创建的设备 [out]
    
    	int EnterMsgLoop(bool (*ptr_display)(float timeDelta));
    
    	LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
    
    	template<class T> void Release(T t)
    	{
    		if(t)
    		{
    			t->Release();
    			t = NULL;
    		}
    	}
    
    	template<class T> void Delete(T t)
    	{
    		if(t)
    		{
    			delete t;
    			t = NULL;
    		}
    	}
    }
    

    函数Init3D创建程序的主窗口,并进行D3D的初始化工作,初始化完成返回IDirect3Device9的指针。
    EnterMsgLoop 进入窗口的消息循环,它的参数是一个显示函数,该函数是用于图形的绘制。
    Release 用于释放一个COM接口
    Delete 用于删除一个对象
    WndProc 是窗口的消息处理函数。

    2.1 乱码处理

    VS中的项目编码默认的是Unicode,这样在进行一些字符串处理(例如设置窗口标题)非常容易出现乱码。这就需要加入头文件tchar.h

    #include <tchar.h>
    

    在使用字符串的时候使用宏_T转换就可以避免乱码,例如:

    wc.lpszClassName = _T("Direct3D9App");
    

    2.2 PeekMessage 和 GetMessage的区别

    在该框架中,进入窗口循环的代码如下:

    int d3d::EnterMsgLoop(bool (*ptr_display)(float timeDelta))
    {
    	MSG msg;
    	::ZeroMemory(&msg,sizeof(MSG));
    
    	static float lastTime = (float)timeGetTime();
    
    	while(msg.message != WM_QUIT)
    	{
    		if(::PeekMessage(&msg,0,0,0,PM_REMOVE))
    		{
    			::TranslateMessage(&msg);
    			::DispatchMessage(&msg);
    		}
    		else
    		{
    			float currTime = static_cast<float>(timeGetTime());
    			float timeDelta = (currTime - lastTime) * 0.001f;
    
    			ptr_display(timeDelta);
    
    			lastTime = currTime;
    		}
    	}
    	return msg.wParam;
    }
    

    在上述代码中取得消息使用的是PeekMessage而不是常用的GetMessagePeekMessageGetMessage都是从消息队列中抓取消息,如果消息队列为空,程序的主线程会被操作系统挂起。过一段时间,当操作系统再次执行该线程时,如果消息队列仍然为空,这时这两个函数的行为则就不一样了:

    • GetMessage 会直接返回,阻塞当前线程,操作系统去执行其他的线程。
    • PeekMessage 则不同,它会取回控制权,使得当前线程再执行一段时间。上面的代码中,消息队列如果为空就执行display函数绘制图形。

    3 总结

    本文是龙书第二章的学习笔记,总结了D3D初始化的4个步骤,并对龙书中提供的代码框架作为简单分析。

  • 相关阅读:
    使用python javaSerializationTools模块拼接生成 8u20 Gadget
    随想与心理咨询师和朋友倾诉的区别 po
    Power BI 中怎么在dashboard中展示parameter参数的值? po
    游戏Batman: Arkham Asylum GOTY Launcher 蝙蝠侠 阿甘姆疯人院年度版 WIN10报错:未能加载文件或程ctx.di序集 Microsoft.directX.Direct3D 解决方案 po
    python sublime console 运行 交互输入Yes or No ( SublimeREPL插件) po
    python Browser automation with selenium 网页自动化测试 练习 po
    sublime python3的配置文件python3.sublimebuild存放位置 po
    HTTP 缓存机制介绍
    Visual Studio 2019 常见问题及解决办法
    将博客搬至CSDN
  • 原文地址:https://www.cnblogs.com/wangguchangqing/p/5413781.html
Copyright © 2020-2023  润新知