D3D学习摘记(I)中

 Note:这是(I)的中篇,并不是一篇独立的主题。它的上篇在这里

 

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。

 

MultiSampleType:多采样类型。没有太多的体会。一般是 D3DMULTISAMPLE_NONE。注意只有当SwapEffect的值为D3DSWAPEFFECT_DISCARD时,该分量才有效。MultiSampleQuality:多采样质量。值位于0到1之间。当上面的MultiSamplleType有设定时该分量才有效。

 

SwapEffect:指缓冲区的交换方式。当前后台缓冲区交换后,换下来的那块缓冲区的数据该怎么处理?可以复制,或者丢弃。不同的值效果不同。一般是D3DSWAPEFFECT_DISCARD,表示丢弃。这时就会有新的数据直接覆盖填充这块缓冲区。

hDeviceWindow:一个窗口句柄会有窗口位置和大小等信息,等到绘制时,后台缓冲区会向前台缓冲区复制数据。而这个分量可以指引D3D设备,让前台缓冲区往屏幕的哪个地方绘制多大的数据。一般都是当前窗口。如果选择0(默认的),就是指当前窗口了。

Windowed:指定是否是窗口模式。true表示窗口模式,false表示全屏模式。

EnableAutoDepthStencil:是否开启深度缓存和模板缓存。true表示开启,flase表示不开启。有关深度缓存和模板缓存的知识另行查阅。

AutoDepthStencilForamt: 如果开启了深度缓存和模板缓存,指定它们的格式。比如指定一个像素的深度位是几位、模板缓存为是几位。注意它和BackBufferFormat用的是同一种枚举类型,只不过取值范围不同而已。

Flags:旗标。没什么太多的体会。一般为0。

FullScreen_RefreshRateInHz:全屏模式下的屏幕刷新率。如果窗口模式,则必须为0。一般设为0即可。

 

PresentationInterval:缓冲区交换频率。一般选择D3DPRESENT_INTERVAL_IMMEDIATE(即为0)即可。表示数据准备好后不要等待,立即交换。

 

 

当D3D设备对象创建好后,我们的初始化也就完成了。同时InitD3D函数也就写完了。虽然实际上没做多少事,但需要讲解的东西挺多。再稍稍总结一遍。首先,我们调用Direct3DCreate9函数,而该函数又是调用COM接口来创建了一个D3D对象,然后我们再利用这个D3D对象创建了一个D3D设备对象(它调用的也同样是COM接口)。创建D3D设备对象时,我们需要对设备做一些配置(设备类型、顶点处理方式、显示参数配置,等等),而其中显示参数配置因为参数众多,不得不专门列一个结构体出来设定。因此创建一个D3DPRESENT_PARAMETERS型的d3dpp变量设定各个分量值(缓冲区大小、数据格式、交换方式、全屏/窗口,等等)。然后将其作为创建D3D设备对象的一个参数注入到CreateDevice中。ok,搞定收工。

(未完待续)

 

 
参考资料:

 

http://dev.gameres.com/Program/Visual/3D/D3DFirst_1.htm

原文地址:https://www.cnblogs.com/lookof/p/1561559.html