呼,终于开始进行正式的D3D编程复习了。不再讲述那些理论性的基本概念了。- -

使用D3D绘制图形

首先我们还是需要简单了解一下D3D程序的基本框架,就象我在说Win编程时一样,我们需要一个框架来协助我们了解整体的机制。

首先创建Windows窗口—》初始化D3D程序—》处理消息循环—》物体图形显示—-》结束D3D程序 。 怎么样,很简单吧,我们将处理消息循环和物体图形显示这里加上一个死循环,这样我们就获得了一组时刻根据消息进行改变的图形显示了。

创建Windows窗口我们已经说过了,从WINMAIN入口开始,我们先定义一个WNDCLASS/EX的结构,在其中控制窗口风格,消息函数名,类名等相关窗口信息。之后注册窗口类,创建窗口,我们初始部分就做完了,开始初始化D3D程序。

  1. 首先我们需要创建D3D接口指针。

因为DX基于COM机制,我先前说过,COM机制中我们无法直接获得COM对象,它不提供类的成员属性,我们必须通过其接口函数进行访问,所以我们必须先获得D3D的接口。

我们首先声明一个D3D指针

LPDIRECT3D9 g_pD3D = NULL; //我使用的是DX9

之后我们对它进行获取赋值

g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );

  1. 我们需要获得显卡设备信息

我们呀创建自己的D3D设备时,需要考虑到我们显卡的显示模式是否支持,当然我们本机的显卡信息可以很轻松的获取,但是我们的游戏不会仅仅是自己来玩,我们需要获取玩家的显卡信息,并且对其进行判断。

这里我们使用下面函数来获得用户当前显卡信息

 HRESULT GetAdapterDisplayMode
 (
 UINT Adapter,   //指定显示卡序列号
 D3DDISPLAYMODE *pMode       //存储显示模式的指针
 );

通常我们的计算机可能不止一块显卡,我们函数的第一个参数就是来控制到底是获得哪块显卡的信息的,一般来说,我们可以使用DEFAULT,代表我们正准备获得当前用户桌面使用的显卡信息。

我们代码中的pMode中将存放的就是显卡信息,其结构如下:

typedef struct _D3DDISPLAYMODE
  {
      UINT Width;   //屏幕宽度
      UINT Height;  //屏幕高度
      UINT RefreshRate;  //屏幕刷新率
      D3DFORMAT Format;  //屏幕显示的像素格式
  } D3DDISPLAYMODE;

其中从宽度和高度可以获得显卡支持的屏幕大小,一般是1024X768,800X600的比较多,刷新率多是60,85左右,象素格式是代表显卡支持的色位是32位还是16位,是什么样的32位象素呢?是A8R8G6B8还是其他的?我们拿A8R8G8B8作例,其中A代表Alpha通道,即半透明度,R代表RED,G–GREEN,B–BLUE三种颜色。后面的8代表它们各自在显卡中占的位数。当然显卡可能支持其他的象素格式,我从网上摘录常见格式如下

  • D3DFMT_UNKNOWN 未知的像素格式
  • D3DFMT_R8G8B8 24位色表示方式,即每个像素的RGB值各占8位
  • D3DFMT_A8R8G8B8 32位色,在24位的基础上添加8位来表示透明程度
  • D3DFMT_X8R8G8B8 32位色,在24位的基础上添加了8位保留位
  • D3DFMT_R5G6B5 16位色,R占5位,G占6位,B占5位
  • D3DFMT_X1R5G5B5 16位色,RGB各占5位,添加了一位保留位
  • D3DFMT_A1R5G5B5 16位色,RGB各占5位,添加了一位表示半透明
  • D3DFMT_A4R4G4B4 16位色,RGBA各占4位
  • D3DFMT_R3G3B2 8位色,R占3位,G占3位,B占2位
  • D3DFMT_A8 只用8位表示半透明
  • D3DFMT_A8R3G3B2 16位色,R占3位,G占3位,B占2位,A占8位
  • D3DFMT_A8P8 16位色,8位代表半透明,8位颜色索引值代表颜色
  • D3DFMT_P8 8位色,用颜色索引值表示D3DFMT_L88位色,只表示亮度
  • D3DFMT_L16 16位色,只表示亮度

我们在程序中就可以这么写来获得显卡的支持模式:

 //获取显示设备显示模式
 D3DDISPLAYMODE d3ddm;
 if(FAILED(g_pD3D->GetAdapterDisplayMode(
               D3DADAPTER_DEFAULT, &d3ddm)))
  {
         return E_FAIL;
 }
  1. 我们已经得知显卡支持的模式了,我们需要建立一个D3D对象来操控我们的整个程序。

代码如下:

HRESULT CreateDevice(
    UINT Adapter,                 //显示卡序列号 
    D3DDEVTYPE DeviceType,  //Direct3D设备类型
    HWND hFocusWindow,  //所属窗口句柄
    DWORD BehaviorFlags,  //Direct3D设备进行3D运算的方式
    D3DPRESENT_PARAMETERS *pPresentationParameters,
    //用于存储Direct3D设备的相关信息的指针
    IDirect3DDevice9** ppReturnedDeviceInterface  
    //返回Direct3D设备接口指针的地址
);
  1. 其中第一个参数依旧是D3DADAPTER_DEFAULT锁定当前使用的显卡。

  2. 第二个参数是我们D3D设备的类型,一般参数有以下三种

  • D3DDEVTYPE_HAL 硬件抽象层,通过显示硬件来完成图形渲染工作
  • D3DDEVTYPE_REF 参考光栅器, 一般用于测试显示卡不支持的Direct3D功能
  • D3DDEVTYPE_SW 用于支持第三方软件

还记得我之前提到过的HAL机制吗?我们D3D可以由硬件显卡实现,还可以通过软件来模拟,而这里就是控制它的。通常我们应当设置为HAL硬件来实现,仅仅当我们硬件无法支持渲染时,才使用REF应付一下,它会非常卡,尽量避免尝试。SW则是高级程序员使用自己的一套算法来实现渲染的模拟,这需要了解显卡底层的知识,T T某志这里还不会

  1. 第三个参数是窗口句柄啦,我们在创建WIN窗口的时候就可以获得

  2. 第四个参数是D3D设备运算模式,一般有下列6种可选

  • D3DCREATE_FPU_PRESERVE //激活双精度浮点运算或浮点运算,并进行异常检测,设置该项会降低系统性能
  • D3DCREATE_MULTITHREADED //支持多线程绘制,设置该项会降低系统性能
  • D3DCREATE_PUREDEVICE //禁用Direct3D的Get*()函数,禁止Direct3D使用虚拟设备模拟顶点运算
  • D3DCREATE_HARDWARE_VERTEXPROCESSING //由Direct3D硬件进行顶点运算
  • D3DCREATE_SOFTWARE_VERTEXPROCESSING //由Direct3D软件进行顶点运算
  • D3DCREATE_MIXED_VERTEXPROCESSING //由硬件软件混合方式进行顶点运算

某志这里推荐在调试时使用最后一个MIXED,它是说系统将我们的顶点运算优先由硬件运算,当我们硬件无法承载运算时,将适当的移处一些部分交给软件进行模拟顶点运算,是比较合理的,若你很了解自己硬件的信息,并确信它能够进行所有的顶点运算时候,可以使用HARDWARE,PUREDEVICE是纯硬件运算,很容易导致一些异常后果,不推荐。

5:第5个参数是一个存储D3D设备信息的结构,就象Window窗口创建前的WNDCLASS类似,我们在其中说明当前即将创建D3D设备的一些信息,它的结构体声明如下:

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是后台缓存的大小。

Format是后台缓存的象素格式,我们在之前已经获得了显卡的支持格式,我们在这里传入其中一种就好。

BackBufferCount是说自定义后台缓存的页面个数,通常来说,窗口模式的游戏,我们设置为1,全屏模式的游戏,我们设置为0,原因我简单说一下,窗口模式游戏下,我们有可能游戏窗口被其他的窗口遮挡住,此时我们需要多设置一个后台缓冲来存放之前的图象,当遮挡的窗口移开后,我们将不必重新绘制后台缓冲区,仅将记忆中的那个缓冲区放到前台就可以恢复我们的游戏界面了,而全屏模式下,我们不必要去考虑这一点,设置为0,并不是说没有后台缓冲,系统会默认给我们一个的,设置为1,是我们在系统给我们的后台缓冲之外,再新设置一个后台缓冲。

MultiSampleType是我们对后台缓冲的处理模式,我们可以采取以下几种方式:

  • D3DSWAPEFFECT_DISCARD 后台缓冲区复制到前台时, 清除后台缓冲区内容
  • D3DSWAPEFFECT_FLIP 后台缓冲区内容复制后,保持不变。有多个后台缓冲区时使用
  • D3DSWAPEFFECT_COPY 后台缓冲区内容复制后,保持不变。只有1个后台缓冲区时使用
  • D3DSWAPEFFECT_FORCE_DWORD 强迫该值作为32位存储,通常不用

可以看出来,当全屏时我们应当设置此参数为DISCARD,晴空后台缓冲,并且在上面进行图象的重绘。当我们窗口化,我们设置为COPY,它将会把刚撤掉的后台缓冲进行一份备份,当游戏屏幕被别的窗口遮挡后,我们可以直接拿COPY过的缓冲调到前台,免去重新绘制的步骤。而设置多重后台缓冲,主要是防止垂直同步,因为我们的缓冲绘制过程是一行一行的绘制的,当我们调用后台缓冲到前台时,若我们没有绘制完毕,就会出现这样的现象,上半部分的图象已经更新绘制了,而下面的图象却因没来得及绘制而保持上次的图象,当我们设置的后台缓冲页面比较多的时候,就可以避免这种情况。当这个缓冲未绘制完成时,系统将放弃它,先拿其他的缓冲顶替上去,就不会出现垂直同步的问题了。

hDeviceWindow用于指定图形绘制窗口的句柄,当它的取值为NULL时,表示使用当前被激活的窗口。

Windowed指明是否需要使用窗口模式,值为TRUE表示使用窗口模式,值为FALSE表示使用全屏模式。这个当然要和前面两个提到的参数进行相应设置咯

EnableAutoDepthStencil用于使用深度缓冲。深度缓冲的东西,我会接下来详细说明,暂时我们就用NULL吧

AutoDepthStencilFormat用于指定深度缓冲的模式。依旧是NULL。

Flags用于指定深度缓冲的标识。还是NULL。

FullScreen_RefreshRateInHz在全屏模式时使用,指定屏幕的刷新率。在窗口模式下,它的值必须为0(记住,是必须!);在全屏模式下,通常取D3DPRESENT_RATE_DEFAULT值,表示显示器屏幕刷新率与前后台缓存交换速率相同。也可以取D3DPRESENT_RATE_UNLIMITED,表示图形绘制结束后立刻将内容复制到前台缓存。

PresentationInterval用于指定屏幕的翻转模式,即前后台缓冲区的交换频率。在窗口模式下,其取值为0 。全屏模式下可以取D3DPRESENT_INTERVAL_ONE或者D3DPRESENT_INTERVAL_DEFAULT(其实DEFAULT和ONE完全无区别),它也是避免垂直同步的,即当我们后台缓冲区未更新绘制完毕之前,我们是不会将其放置到前台的,所以我们玩一些3D游戏时,当我们显卡显存比较小,或者机器卡,网线卡的时候,我们会发现屏幕是一跳一跳的,跳桢现象就是因为我们的后台缓冲还没有渲染完,系统不允许放置到前台,而导致前台缓冲区迟迟换不下来,等更替时,我们可能已经进行了很多操作的原因。


呃,请记住D3D基本框架,我们下面继续讲述,新浪每个日志仅支持10000字,也只能分段发帖了