InitD3D函数做了些什么
在具体分析代码前,讲下D3D环境的使用逻辑。在初始化一个D3D环境时,一般只需要做两步工作。第一,创建一个D3D对象;第二,创建一个D3D设备对象。其中,和我们联系最为紧密的是D3D设备对象,它负责显示设备的各个参数设置、修改缓冲区数据等等,是具体工作的实施者。D3D对象呢?对我们而言,它除了创建D3D设备对象,没有什么特别的意义。
一个D3D设备接口可以简单的认为是本机一块显卡的抽象,它包含了显卡所有的硬件参数及状态值,比如说,显卡显存的数量和起始的线性地址,是否支持深度缓冲(Depth Buffer),雾化(Fog),纹理(Texture) 及MipMap等。
现在我们回顾一下代码。
1 HRESULT InitD3D( HWND hWnd )
2 {
3 // Create the D3D object, which is needed to create the D3DDevice.
4 if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
5 return E_FAIL;
6
7 // Set up the structure used to create the D3DDevice. Most parameters are
8 // zeroed out. We set Windowed to TRUE, since we want to do D3D in a
9 // window, and then set the SwapEffect to "discard", which is the most
10 // efficient method of presenting the back buffer to the display. And
11 // we request a back buffer format that matches the current desktop display
12 // format.
13 D3DPRESENT_PARAMETERS d3dpp;
14 ZeroMemory( &d3dpp, sizeof(d3dpp) );
15 d3dpp.Windowed = TRUE;
16 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
17 d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
18
19 // Create the Direct3D device. Here we are using the default adapter (most
20 // systems only have one, unless they have multiple graphics hardware cards
21 // installed) and requesting the HAL (which is saying we want the hardware
22 // device rather than a software one). Software vertex processing is
23 // specified since we know it will work on all cards. On cards that support
24 // hardware vertex processing, though, we would see a big performance gain
25 // by specifying hardware vertex processing.
26 if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
27 D3DCREATE_SOFTWARE_VERTEXPROCESSING,
28 &d3dpp, &g_pd3dDevice ) ) )
29 {
30 return E_FAIL;
31 }
32
33 // Device state would normally be set here
34
35 return S_OK;
36 }
g_pD3D是一个全局的LPDIRECT3D9 型的变量。从SDK中可以查到,LPDIRECT3D9 就是一个IDirect3D9*型的指针,它指向将要创建好的D3D对象。创建工作由函数Direct3DCreate9负责。它接收一个枚举变量D3D_SDK_VERSION(目前它只接受该值 ) ,返回一个创建好的D3D对象。
为什么这个指针的首字母有个I?这表明,这是一个接口(Interface)。事实上,“D3D对象”以及很多D3D资源都是通过调取COM(Component Object Model)接口获得的。有关COM的知识不在本文讲解范围之内,应该通过其他途径了解它,比如这里(实际上我也仅仅知道它“是什么”)。
接下来要做的是创建D3D设备对象。该对象是通过D3D对象的CreateDeviece方法创建的。我们看一下SDK中的关于这个方法的介绍。
HRESULT CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS *pPresentationParameters,
IDirect3DDevice9 **ppReturnedDeviceInterface );
说一下我对这些参数的理解。
Adapter : 指定显示设备的编号。主设备的值永远是“D3DADAPTER_DEFAULT”。(对于单显示器的用户(大部分是这样),直接填该值即可)
Device Type:指定设备类型。前面提过,D3D提供的种种功能,具体实现是由显卡厂商做的HAL(Hard Abstract Layer)负责的。然而如果有些显卡因为老或者没被微软“宠幸”的关系,不支持D3D怎么办?没问题,D3D设定了一种“软件模拟”的设备类型。你可以用主存和cpu模拟任何本应作用于显卡上的计算,而代价就是拖慢系统(cpu和主存的资源被这些计算占据)。D3D还设定了一种比较特别的类型:假定你是一名比较不幸的开发者。假如如此,你的显卡不支持D3D,然而你的用户却支持。这时你就可以用“引用类型”,在开发时依然可以用软件模拟,而在发布时换成了硬件负责。总结一下,上面讲了三种设备类型:硬件、软件、引用。对应的值分别是:D3DDEVTYPE_HAL、D3DDEVTYPE_SW和D3DDEVTYPE_REF。例子用的是D3DDEVTYPE_HAL。
hFocusWindow:一个窗口句柄,指定该D3D设备对象所联系的win32窗口。一般是当前窗口。例子中是hWnd。
BehaviorFlags:行为旗标。常用有D3DCREATE_SOFTWARE_VERTEXPROCESSING和D3DCREATE_HARDWARE_VERTEXPROCESSING。分别指示“顶点处理”的工作是由软件模拟还是硬件管线计算。例子中用的是D3DCREATE_SOFTWARE_VERTEXPROCESSING。
什么是“顶点处理”?这和图形学有密切的联系。我们知道3D图形最初的数据都是关于顶点信息的,比如一个顶点的位置坐标、颜色、透明度、法线等等。三个顶点能构成一个三角形,这个三角形的颜色由最后的“光栅化”阶段根据三个顶点的相关信息,通过插值算法逐一填充落在三角形内的每个像素。而顶点的数据(尤其是坐标)也会随着渲染的不同阶段而发生变化。比如一开始是该顶点本身的局部坐标,接下来会变换到世界坐标,再变换到视图坐标,然后是投影坐标…每变换一次,顶点的数据就会改变一次。另外光照计算时也会影响到顶点的颜色值。所有关于这些顶点信息的处理,是发生在什么地方呢?这就是这个参数所指定的意义。如果你选择了D3DCREATE_SOFTWARE_VERTEXPROCESSING,就会用软件模拟的方式,令cpu和主存处理这样的事情;而如果选择了D3DCREATE_HARDWARE_VERTEXPROCESSING,就会用显卡上的专用硬件来进行专门的处理,从而解放cpu和主存资源。当然,正如前面提过,前提是这块显卡必须具备这样的功能。也就是说,你必须清楚自己的显卡有没有这样的支持才行(一般现在的独立显卡都有)。D3D中有专门的函数可以查看:D3D对象中有一个GetDeviceCaps方法专司此职。请自行查阅相关资料。
pPresentationParameters: 这是一个指针,指向一个对“显示”方面各种参数(主要是缓冲区)进行设定的结构体。待会具体讲解这个参数。
ppReturnedDeviceInterface:指向一个D3D设备对象的指针的指针。用它来得到创建好的D3D设备对象。因此该指针你得事先声明。
我们现在来讲pPresentationParameters这个结构的内容。它是一个D3DPRESENT_PARAMETERS型的指针。关于该类型的介绍还是要查看SDK。这里不得不抱怨的是,虽然它仅仅是创建D3D设备对象时需要指定的一个参数,但它本身却是一个更加无比繁杂的结构…是不是头有点大了?不过还好,这些值默认都被初始化为0,而0都是这些参数的常态。如果没有特别要求,一般只需要指定Windowed、SwapEffect和BackBufferFormat三个分量即可(例子就是如此)。不过为了稍稍刨根究底,还是全面扫一下比较好。
typedef struct _D3DPRESENT_PARAMETERS_ {
UINT BackBufferWidth, BackBufferHeight;
D3DFORMAT BackBufferFormat;
UINT BackBufferCount;
D3DMULTISAMPLE_TYPE MultiSampleType;
DWORD MultiSampleQuality;
D3DSWAPEFFECT SwapEffect;
HWND hDeviceWindow;
BOOL Windowed;
BOOL EnableAutoDepthStencil;
D3DFORMAT AutoDepthStencilFormat;
DWORD Flags;
UINT FullScreen_RefreshRateInHz;
UINT PresentationInterval;
} D3DPRESENT_PARAMETERS;
说下我的理解。
BackBufferWidth, BackBufferHeight:顾名思义,这是指后台缓冲区的宽度和高度,单位是像素。一般这和窗口大小是相等的(指窗口模式下)。
BackBufferFormat:指后台缓冲区的像素的数据格式。比如指定RGBA分别占几位。而一般地,在窗口模式下,使用D3DFMT_UNKNOWN会使用当前显示模式使用的像素数据格式。我们使用D3DFMT_UNKNOWN即可。
BackBufferCount:后台缓冲区的个数,可选值有0,1,2,3. 0会被当做1对待。一般是1。
hDeviceWindow:一个窗口句柄会有窗口位置和大小等信息,等到绘制时,后台缓冲区会向前台缓冲区复制数据。而这个分量可以指引D3D设备,让前台缓冲区往屏幕的哪个地方绘制多大的数据。一般都是当前窗口。如果选择0(默认的),就是指当前窗口了。
Windowed:指定是否是窗口模式。true表示窗口模式,false表示全屏模式。
EnableAutoDepthStencil:是否开启深度缓存和模板缓存。true表示开启,flase表示不开启。有关深度缓存和模板缓存的知识另行查阅。
AutoDepthStencilForamt: 如果开启了深度缓存和模板缓存,指定它们的格式。比如指定一个像素的深度位是几位、模板缓存为是几位。注意它和BackBufferFormat用的是同一种枚举类型,只不过取值范围不同而已。
Flags:旗标。没什么太多的体会。一般为0。
FullScreen_RefreshRateInHz:全屏模式下的屏幕刷新率。如果窗口模式,则必须为0。一般设为0即可。
当D3D设备对象创建好后,我们的初始化也就完成了。同时InitD3D函数也就写完了。虽然实际上没做多少事,但需要讲解的东西挺多。再稍稍总结一遍。首先,我们调用Direct3DCreate9函数,而该函数又是调用COM接口来创建了一个D3D对象,然后我们再利用这个D3D对象创建了一个D3D设备对象(它调用的也同样是COM接口)。创建D3D设备对象时,我们需要对设备做一些配置(设备类型、顶点处理方式、显示参数配置,等等),而其中显示参数配置因为参数众多,不得不专门列一个结构体出来设定。因此创建一个D3DPRESENT_PARAMETERS型的d3dpp变量设定各个分量值(缓冲区大小、数据格式、交换方式、全屏/窗口,等等)。然后将其作为创建D3D设备对象的一个参数注入到CreateDevice中。ok,搞定收工。
(未完待续)