学习目标:
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中最重要的方法如下:
多重采样
用像素矩阵表示图像时往往会出现块状效应,多重采样便是一项用于平滑块状图像的技术。
D3DMULTISAMPLE_TYPE 枚举类型包含了一系列枚举常量值,用于表示对表面进行多重采样的级别,这些值包括:
多重采样会显著降低应用程序的运行速度。如果希望使用该技术,务必使用 IDirect3D9::CheckDeviceMultiSampleType 方法检查显卡是否支持所希望采用的多重采样类型,并验证由该类型的多重采样得到图形质量水平是否理想。
像素格式
创建表面或纹理时,常常需要指定这些Derect3D资源的像素格式,像素格式可用D3DFORMAT枚举类型的枚举常量来定义,
下面是一些常见的格式:
注意:前3种格式较为通用,并为大多数硬件所支持。浮点型像素格式以及其他格式没有得到广泛的支持,使用前务必验证显卡是否支持某种具体的像素格式。
内存池
表面和其他的 Direct3D 资源可以放入许多类型的内存池中,内存池的类型可用 D3DPOOL 枚举类型来表示。可用的内存池包括:
交换链和页面置换
Derect3D维护着一个表面集合,该集合通常由2到3个表面组成,称之为交换链。该集合用接口IDirect3DSwapChain9来表示。我们不追究该接口的细节,因为它是由Direct3D负责管理的,而我们几乎不用对其操作,这里只对其用途进行概述。
交换链和页面置换技术主要用于生成更平滑的动画。
前台缓存与一个或多个后台缓存组成信息交换模式形成交换链。前台缓存对应于当前显示器中显示的图像,后台缓存是准备提交给前台缓存的存在,可以是1个或多个。就像现实世界中的接龙一样,把东西提交给前边的人然后在接下后边的东西。
为什么要这么做呢,原因是显示器并不是立即显示由前端缓存所表示的内容,例如,当显示器刷新频率为60Hz时,应用程序绘制图像的速度可能要快于显示器,绘制多出的内容就交由缓存区中,程序继续允许,不受显示器的影响。
完成绘制功能的程序结构为:
深度缓存
深度缓存是一个只包含有特定像素的深度信息而不含图像数据的表面。深度缓存为最终绘制的图像中的每一个像素都保留拉一个深度项。所以,当绘制的图像分辨率为640x180时,深度缓存中将有640x480个深度项。
Direct3D 为了判定某一物体的哪些像素位于另一个物体之前,使用了一项称为深度缓存或 z-缓存的技术。
深度缓存用于计算每个像素的深度值并进行深度测试。深度测试的基本内容是依据深度值让处于同一位置的不同像素进行竞争,以选出应写入该位置的像素,距离摄像机最近的像素获胜,并被写入深度缓存的相应位置上。这样做是合理的,因为距离摄像机近的像素一定会将其后方的像素遮挡。
深度缓存的格式决定了深度缓存的精度。24位的深度缓存要比16位的深度缓存精确很多,虽然Direct3D提供拉32位的深度缓存,但一般情形下,多数应用程序使用24位深度缓存已足以获得满意的效果。
顶点运算
顶点是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 的初始化过程可分解为如下步骤。
获取接口 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文档 的相关部分。
填充 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
);
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;
}