当前位置: 首页 > 工具软件 > D3D9On12 > 使用案例 >

D3D9学习笔记之初始化Derect3D

王岳
2023-12-01

学习目标:

  • Direct3D 与图形硬件的交互方式
  • COM在 Driect3D 中扮演的角色
  • 基本的图形学概念,如2D图像的存储方式,页面置换和深度缓存
  • 初始化 Derect3D
  • 一种通用的初始化程序结构

Direct3D 概述
Direct3D 是一套 底层图像接口(API) ,借助该API,我们能够利用显卡来绘制 3D 场景。可以把 Direct3D 视作应用程序(最终游戏)与显卡交互的中介,也就是说借助该套API来操控显卡,利用显卡对图像处理的优势来为自己的产品加速。
例如,要命令显卡执行清屏操作,应用程序可调用 Direct3D 方法 IDirect3DDevice9::Clear。

Direct3D与应用程序,显卡之间的关系:
应用程 -> Direct3D -> HAL -> 图形设备(显卡)

Direct3D 是一套已经定义好的,提供给应用程序/编程人员的接口和函数。这些接口代表了当前版本的 Direct3D 所支持的全部功能。当然,如果你的显卡不支持 Direct3D 的某些功能,那么Direct3D也无法做到,所有的接口基于你的显卡支持与否。

HAL(Hardware Abstraction Layer,硬件抽象层),由于显卡种类繁多,每种显卡的性能和实现功能的机理不同,所以Derect3D无法与显卡直接交互,于是显卡厂商就需要实现一个 HAL,HAL是一套指示显卡完成某些操作的代码集。按照这种方式,Direct3D就可不必了解显卡的具体细节。所以Direct3D的制定可以独立于具体的显卡。

HAL不支持 显卡厂商把显卡所支持的全部功能实现到HAL中,有些 Direct3D 支持,但显卡不支持的功能无法在HAL中实现。调用一个没有在HAL中实现的Direct3D函数会导致调用失败,除非它是一种顶点处理运算,并且用户已经指定了使用软件顶点运算方式。在这种情况下,用户期望的功能便可由Direct3D运行时 Direct3D runtime以软件运算方式来模拟。所以使用少数种类的显卡所支持的Direct3D高级功能时,必须验证你的显卡是否支持该功能(具体在下面的硬件性能中说明)。

REF设备 该套设备的作用是用于实现Direct3D的全部功能,即你的显卡不支持Direct3D的某些功能时,这时可以用这套设备完成,该套设备是以CPU软件模拟的方式完成显卡没有的功能,所以该套设备的速度很慢,仅适合用于开发测试阶段,不能发布给最终用户。

D3DDEVTYPE 与 HAL,REF 在程序代码中,HAL设备用值 D3DDEVTYPE_HAL 来指定,该值是 D3DDEVTYPE 枚举类型的一个成员。类似的,REF 设备用值 D3DDEVTYPE_REF 来指定,该值也是 D3DDEVTYPE 枚举类型的一个成员。这些类型非常重要,需要铭记于心,因为在创建设备时,必须指定使用哪种设备类型。

COM(组件对象模型)
COM是一项能够使DirectX独立于编程语言并具备向下兼容的技术,我们常称COM对象为接口,可将其视为一个C++类来使用,我们所必须知道的仅仅是如何通过某个函数或另一个COM接口的方法来获取指向某一COM接口的指针,创建COM接口时不可使用C++的关键字new,此外,使用完一个接口,应调用该接口相应的Release方法而不是C++的delete。COM对象能够对其所使用的内存实施自治。

预备知识

表面
表面是Direct3D主要用于存储2D图像数据的一个像素矩阵。虽然我们可以将表面存储的数据视为一个矩阵,但像素数据实际上存储在一个线性数组中。
表面的宽度和高度都用像素的来度量。跨度则用字节来度量。
在代码中,用IDirect3DSurface9来描述表面。该接口提供拉几种直接从表面读取和写入数据的方法,以及一种获取表面相关信息的方法。
接口IDerect3DSurface9中最重要的方法如下:

  • LockRect 获取指向表面存储区的指针。用过指针运算,可对表面中的每一个像素进行读写操作。
  • UnlockRect 如果调用拉LockRect 方法,而且已执行完访问表面存储区的操作,必须调用该方法接触对表面存储区的锁定。
  • GetDesc 该方法可通过填充结构D3DSURFACE_DESC 来获取该表面的描述信息。

多重采样
用像素矩阵表示图像时往往会出现块状效应,多重采样便是一项用于平滑块状图像的技术。
D3DMULTISAMPLE_TYPE 枚举类型包含了一系列枚举常量值,用于表示对表面进行多重采样的级别,这些值包括:

  • D3DMULTISAMPLE_NONE 禁用多重采样。
  • D3DMULTISAMPLE_1_SAMPLE … D3DMULTISAMPLE_16_SAMPLE 指定拉从1~16级的多重采样。
  • 另外还有一个与特定多重采样类型相关的质量水平,该水平值用DWORD类型描述。

多重采样会显著降低应用程序的运行速度。如果希望使用该技术,务必使用 IDirect3D9::CheckDeviceMultiSampleType 方法检查显卡是否支持所希望采用的多重采样类型,并验证由该类型的多重采样得到图形质量水平是否理想。

像素格式
创建表面或纹理时,常常需要指定这些Derect3D资源的像素格式,像素格式可用D3DFORMAT枚举类型的枚举常量来定义,
下面是一些常见的格式:

  • D3DFMT_R8G8B8 每个像素由24位组成,自最左端起,每8位分别分配给红色,绿色,蓝色。
  • D3DFMT_X8R8G8B8 每个像素由32位组成,自最左端起,前8位未使用,后24位中每8位分别分配给红色,绿色,蓝色。
  • D3DFMT_A8R8G8B8 每个像素由32位组成,自最左端起,前8位分配给Alpha值,后24位中每8位分别分配给红色,绿色,蓝色。
  • D3DFMT_A16B16G16R16F 每个像素由64位组成,是一种浮点数类型的像素格式。自最左端起,16位分配给Alpha,16位分配给蓝色,8位分配给绿色,8位分配给蓝色。
  • D3DFMT_A32B32G32R32F 每个像素由128位组成,是一种浮点数类型的像素格式。自最左端起,32位分配给Alpha,32位分配给蓝色,32位分配给绿色,32位分配给红色。
  • 想看到Direct3D所支持的所有像素格式列表,请查阅SDK文档中 D3DFORMAT 的相关部分。

注意:前3种格式较为通用,并为大多数硬件所支持。浮点型像素格式以及其他格式没有得到广泛的支持,使用前务必验证显卡是否支持某种具体的像素格式。

内存池
表面和其他的 Direct3D 资源可以放入许多类型的内存池中,内存池的类型可用 D3DPOOL 枚举类型来表示。可用的内存池包括:

  • D3DPOOL_DEFAULT 默认值。该类型的内存池指示 Direct3D 将资源放入最适合该资源类型及其使用方式的存储区中。该存储区可能是显存,AGP存储区或系统存储器。注意:调用函数 IDirect3DDevice9::Reset 之前,必须对默认内存池中的资源进行销毁或释放。上诉函数调用之后,还必须对内存池中的资源重新初始化。
  • D3DPOOL_MANAGE 放入该托管内存池中的资源将交由Direct3D管理,即是:这些资源将根据需要被设备自动转移到显存或AGP存储区中。此外,这些资源将在系统存储区中保留一个备份,这样,必要时Direct3D 会将这些资源自动更新到显存中。
  • D3DPOOL_SYSTEMMEM 指定将资源放入系统存储区中。
  • D3DPOOL_SCRATCH 指定将资源放入系统存储区中。但这些资源不受显卡的约制。所以设备无法访问该类型内存池中的资源。但这些资源之间可以相互复制。

交换链和页面置换
Derect3D维护着一个表面集合,该集合通常由2到3个表面组成,称之为交换链。该集合用接口IDirect3DSwapChain9来表示。我们不追究该接口的细节,因为它是由Direct3D负责管理的,而我们几乎不用对其操作,这里只对其用途进行概述。

交换链和页面置换技术主要用于生成更平滑的动画。
前台缓存与一个或多个后台缓存组成信息交换模式形成交换链。前台缓存对应于当前显示器中显示的图像,后台缓存是准备提交给前台缓存的存在,可以是1个或多个。就像现实世界中的接龙一样,把东西提交给前边的人然后在接下后边的东西。
为什么要这么做呢,原因是显示器并不是立即显示由前端缓存所表示的内容,例如,当显示器刷新频率为60Hz时,应用程序绘制图像的速度可能要快于显示器,绘制多出的内容就交由缓存区中,程序继续允许,不受显示器的影响。
完成绘制功能的程序结构为:

  1. 在后台缓存中进行绘制。
  2. 提交后台缓存的内容。
  3. 回到步骤1.

深度缓存
深度缓存是一个只包含有特定像素的深度信息而不含图像数据的表面。深度缓存为最终绘制的图像中的每一个像素都保留拉一个深度项。所以,当绘制的图像分辨率为640x180时,深度缓存中将有640x480个深度项。
Direct3D 为了判定某一物体的哪些像素位于另一个物体之前,使用了一项称为深度缓存或 z-缓存的技术。
深度缓存用于计算每个像素的深度值并进行深度测试。深度测试的基本内容是依据深度值让处于同一位置的不同像素进行竞争,以选出应写入该位置的像素,距离摄像机最近的像素获胜,并被写入深度缓存的相应位置上。这样做是合理的,因为距离摄像机近的像素一定会将其后方的像素遮挡。
深度缓存的格式决定了深度缓存的精度。24位的深度缓存要比16位的深度缓存精确很多,虽然Direct3D提供拉32位的深度缓存,但一般情形下,多数应用程序使用24位深度缓存已足以获得满意的效果。

  • D3DFMT_D32 指定32位深度缓存。
  • D3DFMT_D24S8 指定了24位深度缓存,其中8位保留供模板缓存使用。
  • D3DFMT_D24X8 仅指定24位深度缓存。
  • D3DFMT_X4S4 指定了24位深度缓存,其中4位保留供模板缓存使用。
  • D3DFMT_D16 仅指定16位深度缓存。
  • 注意:模板缓存是更高级的主题,以后再讨论咯

顶点运算
顶点是3D几何学中的基本元素,在Direct3D中,可用2种不同的方式进行顶点运算,即软件顶点运算硬件顶点运算。无论采用哪种配置的硬件。软件顶点运算总会被支持,所以总是可以使用的,而硬件顶点运算只有得到图形卡的支持方可使用。
应该始终优先考虑硬件顶点运算,使用硬件顶点运算,程序的执行速度会比软件顶点运算快很多。而且硬件顶点运算不占用CPU资源。也就是说软件顶点运算时由CPU工作的,硬件顶点运算是由显卡工作的。
注意:显卡支持硬件顶点运算的另一种等价说法是该显卡支持变换和光照。

设备性能
Direct3D 所提供的每一项性能都对应于结构 D3DCAP9 中的一个数据成员或某一位。使用时,首先以某一具体硬件为基础,初始化一个 D3DCAPS9 类型的实例。然后在程序中通过检查该 D3DCAPS9 实例中相应的数据成员或位来判断该设备是否支持某项特性。
下面的例子对此做了解释。假定我们希望检查某一显卡是否可做硬件顶点运算,通过查阅 D3DCAPS9 的相关 SDK 文档,发现数据成员D3DCAPS9::DevCaps 的 D3DDEVCAPS_HWTRANSFORMANDLIGHT 位可用来表示该设备是否支持变换和光照的硬件计算。
假定 caps 为 D3DCAPS9 的一个实例并已初始化,测试代码如下:

bool supportsHardwareVertexProcessing;
//如果位是“on”,则表示硬件设备支持它
if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLLGHT)
{
	//是的,位是开的,所以支持它。
	supportsHardwareVertexProcessing = true;
}
else
{
	//不,位已关闭,因此不受支持。
	supportsHardwareVertexProcessing = false;
}

注意: DevCaps 表示“设备性能”。
注意: 建议您查阅 D3DCAPS9 相关的 SDK 文档,研究 Direct3D 所提供的设备性能的完整列表。

Direct3D的初始化

Direct3D 的初始化过程可分解为如下步骤。

  1. 获取接口 IDirect3D9 的指针。该接口用于获取系统中显卡设备的信息并创建接口 IDirect3DDevice9,该接口是一个C++对象,代表了我们用来显示3D图形的显卡。
  2. 检查显卡性能(D3DCAPS9),判断显卡是否支持硬件顶点运算。为了创建接口 IDirect3DDevice9,我们必须明确显卡是否支持该功能。
  3. 初始化 D3DPRESENT_PARAMETERS 结构的一个实例。填充该结构用于指定即将创建的 IDirect3DDevice9 的特性。也就是说,创建 IDirect3DDevice9 总是需要一个 D3DPRESENT_PARAMETERS 指定某些特性。
  4. 利用一个已初始化的 D3DPRESENT_PARAMETER 结构创建 IDirect3DDevice9对象(一个C++对象,代表了用来显示图形的显卡)。

获取接口 IDirect3D9的指针
要初始化 IDirect3D9, 首先必须获取指向接口 IDirect3D9 的指针。

IDirect3D9* _d3d9;
_d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

函数 Direct3DCreate9 的参数必须是 D3D_SDK_VERSION,只有如此才能保证程序使用正确的头文件。如果函数调用失败将返回一个NULL指针。
上诉 IDirect3D9 对象主要有两个用途:设备枚举以及创建 IDirect3DDevice9 类型的对象。
设备枚举:指获取系统中可用的每块图像卡的性能,显示模式,格式及其他信息。

验证显卡是否支持顶点运算
创建一个代表显卡的 IDirect3DDevice9 类型对象时,必须指定该对象进行顶点运算的类型。如果可以,我们希望使用硬件顶点运算,但是不是所有显卡都支持顶点运算,所以我们必须首先检查显卡是否支持顶点运算。
要进行检查,必须首先根据主显卡的性能参数初始化一个 IDirect3DDevice9 类型的对象。用如下方法来完成初始化。

HRESULT IDirect3D9::GetDeviceCaps(
	UINT Adapter,				//指定物理显卡的序号
	D3DDEVTYPE DeviceType,		//指定设备类型。硬件设备(D3DDEVTYPE_HAL)或软件设备(D3DDEV_TYPE_REF)
	C3DCAPS9 * pCaps			//返回已初始化的设备性能结构实例
);

接下来就可以对设备性能进行检查了,代码如下:

//使用主显示适配器的功能填充D3DCAPS9结构
D3DCAPS9 caps;
d3d9->GetDeviceCaps(
	D3DADAPTER_DEFAULT, 			//表示主显示适配器
	devictType,						//指定设备类型,通常为D3DDEVTYPE_HAL
	&caps					      	//返回填充的D3DCAPS9结构,其中包含主显示适配器的功能
);

//我们可以使用硬件顶点处理吗?
int vp = 0;
if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
{
	//是的,位是开的,所以支持它。
	vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
else
{
	//不,位已关闭,因此不受支持。
	vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}

注意:我们将 vp 保存供创建 IDirect3DDevice 对象时使用,用它指定顶点运算类型。
注意:预定义的 D3DCREATE_HARDWARE_VERTEXPROCESSING 与 D3DCREATE_SOFTWARE_VERTEXPROCESSING 分别代表硬件顶点运算和软件顶点运算。
注意:如果某个例程不能运行,很可能是因为当前显卡不支持例程中使用的特性,可以尝试切换到REF设备再运行。

填充 D3DPRESENT_PARAMETER 结构
初始化 DIrect3D 的下一个步骤是填充上诉结构的一个实例。
该结构用于指定所要创建的 IDirect3DDevice9 类型对象的一些特性,该结构的定义如下:

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;

    /*对于窗口模式,全屏刷新率inhz必须为零 */
    UINT                FullScreen_RefreshRateInHz;
    UINT                PresentationInterval;
} D3DPRESENT_PARAMETERS;

下列对 D3DPRESENT_PARAMETERS 结构的数据成员进行描述,仅涉及了一些对初学者重要的标记和选项。如果想了解全部的标记和选项,请参考 SDK文档 的相关部分。

  • BackBufferWidth 后台缓存中表面的宽度,单位为像素。
  • BackBufferHeight 后台缓存中表面的高度,单位为像素。
  • BackBufferFormat 后台缓存的像素格式(如32位像素格式:D3DFMT_A8R8G8B8)。
  • BackBufferCount 所需使用的后台缓存个数,通常指定为1,表示只需要一个后台缓存。
  • MultiSampleType 后台缓存所使用的多重采样类型(如禁用多重采样:D3DMULTISAMPLE_NONE)。
  • MultiSampleQuality 多重采样的质量水平(上面禁用多重采样,这里则应为0)。
  • SwapEffect D33SWAPEFFECT 枚举类型的一个成员,该枚举指定拉交换链中的缓存页面置换方式,指定为 D3DSWAPEFFECT_DISCARD 时效率最高。
  • hDeviceWindow 与设备相关的窗口句柄,指定了所要绘制的窗口。
  • Windowed 为 true 时,表示窗口模式,为 false 时,表示全屏模式。
  • EnableAutoDepthStencil 设为 true,则 Direct3D 自动创建并维护深度缓存或模板缓存。
  • AutoDepthStencilFormat 深度缓存或模板缓存的像素格式(例如,用24位表示深度并将8位保留供模板缓存使用,D3DFMT_D24S8)。
  • Flags 一些附加特性,可指定为0或 D3DPRESENTFLAG 集合中的一个成员,其中两个成员比较常用(D3DPRESENTFLAG_LOCKABLE_DEPTHBUFFER 指定可锁定的后台缓存。使用可锁定的后台缓存会降低性能。D3DPRESENTFLAG_DISCARD_DEPTHBUFFER 指定当下一个后台缓存提交时,哪个深度或模板缓存将被丢弃,这样可以提升性能)。
  • FullScreen_RefreshRateInHz 刷新频率,如果使用默认的刷新频率,可指定为 D3DPRESENT_RATE_DEFAULT。
  • PresentationInterval D3DPRESENT 集合的一个成员,要了解完整的合法时间间隔列表,请参考文档。其中两个成员较为常用(D3DPRESENT_INTERVAL_IMMEDIATE 立即提交; D3DPRESENT_INTERVAL_DEFAULT 由 Direct3D 来选择提交频率,通常该值等于刷新频率)。

填充 D3DPRESENT_PARAMETERS 结构的一个例子:

D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth      				=800;
d3dpp.BackBufferHeight					=600;
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								=D3DPRESENTFLAG_DISCARD_DEPTHBUFFER;
d3dpp.FullScreen_RefreshRateInHz		=D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval 				=D3DPRESENT_INTERVAL_IMMEDIATE;

创建 IDirect3DDevice9(显卡) 接口
D3DPRESENT_PARAMETERS 结构填充完毕后,可以用如下方法创建 IDirect3DDevice9 类型的对象。

HRESULT IDirect3D9::CreateDevice(
	UINT Adapter,
	D3DDEVTYPE DeviceType,
	HWND hFocusWindow,
	DWORD BehaviorFlags,
	D3DPRESENT_PARAMETERS *pPresentationParameters,
	IDirect3DDevice9** ppReturnedDeviceInterface 
);
  • Adapter 指定希望用已创建的 IDirect3DDevice9 代表哪块物理显卡。
  • DeviceType 指定需要使用的设备类型(如,硬件设备 D3DDEVTYPE_HAL 或软件设备 D3DDEVTYPE_REF)。
  • hFocusWindow 与设备相关的窗口句柄。通常情况下是指设备所要进行绘制的目标窗口。
  • BehaviorFlags 该参数可为 D3DCREATE_HARDWARE_VERTEXPROCESSING 或 D3DCREATE_SOFTWARE_VERTEXPROCESSING。
  • pPresentationParameters 一个已经完成初始化的 D3DPRESENT_PARAMETERS 类型的实例,该实例定义了设备的一些特性。
  • ppReturnedDeviceInterface 返回所创建的设备。
    下面是该函数的调用示例:
IDirect3DDevice9* device = 0;
HRESULT hr = d3d9->CreateDevice(
	D3DADAPTER_DEFAULT,							//初级的适配器
	D3DDEVTYPE_HAL,								//设备类型
	hwnd,										//与设备关联的窗口
	D3DCREATE_HARDWARE_VERTEXPROCESSING,		//顶点处理类型
	&d3dpp,										//当前参数
	&device										//返回创建的设备
);
if(FAILED(hr))
{
	MessageBox(0,"创建设备失败",0,0);
	return 0;
}
 类似资料: