CSDN博客

img ozx

D3DFrame 入门

发表于2001/6/14 1:23:00  1114人阅读

D3DFrame 入门

作者:Philip Taylor

发布日期:2000 年 5 月 15 日     将于 2000 年 6 月 19 日存档

下载 下载本文的源代码(63.9K)

欢迎关注“Driving DirectX”的第二个问题,MSDN Online Voices 的 DirectX® 专栏。在下面几个专栏中,我想着重讨论 Direct3d(R) (D3D) for DirectX 7.0。对于 D3D 示例和 D3DFrame,好的切入点是“DirectX 软件开发工具包 (SDK)”应用程序框架。所有的 D3D 示例都使用 D3DFrame。


使用公用的框架 D3DFrame,为我们提供了公共平台,在此平台上我们可以展开各种功能的讨论。

一些图形芯片厂商使用 D3DFrame 的变体作为自己的示例;例如 ATI SDK 使用具有某些 ATI 专用附件的 D3DFrame 版本。使用公共框架提供了公共平台,在此平台上可以进行如何启用各种功能的讨论。使用公共框架也使我能够避开 D3D 的一般“基本知识”。这并不意味着 D3DFrame 可以作为产品代码;相反,它只是编写短小精悍示例的“方便”工具而已。

开卷语

在开始说明 D3DFrame 之前,让我们回顾一下 DirectX SDK 基础知识,并提供关于 SDK 开发的共同语言。请确保在您建立的环境中,查找头文件和库文件目录的命令是正确的。常见的问题是这些错误的命令,导致了许许多多编译和链接错误。安装 DirectX SDK 的默认路径是 /mssdk。在安装 SDK 时您可以控制该路径。运行时的头文件和库文件保存在 /mssdk/include 和 /mssdk/lib 中。

SDK HTML Help 是大体了解 DirectX 的主要资源,特别是对 D3D。我在解决了某些问题,以及有了某种想法之后,多次重读了该文档中的段落,于是有了温故知新的感觉,“喔,原来这么回事。”示例代码是学习 DirectX 另一个最好的工具。“Direct3D 立即模式”示例代码保存在 /mssdk/samples/multimedia/d3dim 中。在 /d3dim 下面为 /bin、/include、/lib、/media 和 /src。以准备建立的形式存在的所有示例,可以在 /src 中找到,这取决于 /include 和 /lib,并且使用 /media 中的艺术内容。SDK 安装程序将预先建立的可执行文件植入 /bin 下。我强烈推荐用示例来进行试验,并检验使用它们列出的功能所需的代码。这是快速使用 D3D 的最快的方法。

调试 DirectX 应用程序可能是个难题。让我来介绍几种使工作更轻松的有用的小工具和技术。DXDiag SDK 实用工具提供有关 SDK 安装的信息。一定要对您的系统了如指掌。如果发现任何缺陷,则需要这些信息来报告缺陷。而且,一定要在 SDK 安装时选择调试运行时选项。如此就能保证在调试运行时进行开发,调试运行时包含错误检查和报告,这能真正帮助您解释代码在哪里出现了偏差。调试运行时允许设置启用运行时内部调试的报告级。“DirectX 控制面板”允许开发人员将报告级设置为 0 到 5。我在调试时一般设置为 3,而对于特别严重的缺陷要设置为 5。任何调试程序可以跟踪调试。Visual C++ GUI 调试程序提供了调试输出窗格,专门用来跟踪调试。Visual C++ GUI 调试程序,只有在使用下列系统时,才能调试全屏独占应用程序:

  1. 多监视器系统
  2. 远程调试

在这种思想指导下,通常明智的做法是产生代码并先在窗口模式下调试,然后再专注于全屏幕。

高级视图

让我们现在回到 DirectX 7.0 D3DFrame。D3DFrame 是 Direct3D SDK 示例的示例框架。如果您确实想了解示例是如何工作的 — 不是了解示例中列出的功能 — 必须对 D3DFrame 如何工作有基本了解。首先我会带您进入包含所有必须了解的内容的 D3DFrame 高级视图,并且使用 D3DFrame 编写自己的示例。对于象谚语中的猫一样好奇的人,我将就 D3DFrame 的主要部分给出更详细等级的概要介绍,以便解释如何提供应用程序接口。在随后的讨论中,我将在示例应用 D3DFrame 的同时介绍有关 D3DFrame 的更多内容。


图 1. D3DFrame 项目视图

D3DFrame 由 7 个模块组成:

  1. D3DApp 展示了该示例使用的应用程序接口。
  2. D3DFrame 提供了应用程序接口使用的框架,“在该框架下”进行 voodoo 会很顺利。
  3. D3DEnum 包含对驱动程序、设备和模式的枚举支持。
  4. D3DTextr 提供纹理支持。
  5. D3DFile 提供 x-文件支持。
  6. D3DMath 给出数学实用程序函数。
  7. D3DUtil 包括剩下的其他各种有用的函数。

每个模块都是由一个 .cpp 源文件和 .h 头文件组成。源文件位于 d3dim/src。头文件位于 d3dim/include。框架库位于 d3dim/lib。鉴于本文的目的,我主要讨论 D3DApp 和 D3DFrame。

检查用户编写的函数

D3DApp 模块包含类 CD3DApplication。该类在 D3Dapp.h 文件中,为示例程序发布接口,如下所示:

牋牋 // 应用程序创建的可替代的 3D 场景函数 
  virtual HRESULT OneTimeSceneInit()    { return S_OK; }
  virtual HRESULT InitDeviceObjects()   { return S_OK; }
  virtual HRESULT DeleteDeviceObjects() { return S_OK; }
  virtual HRESULT Render()         { return S_OK; }
  virtual HRESULT FrameMove( FLOAT )    { return S_OK; }
  virtual HRESULT RestoreSurfaces()     { return S_OK; }
  virtual HRESULT FinalCleanup()        { return S_OK; }

要使用示例框架创建新的 D3D 应用程序,只要创建新的项目和这些函数的新的实现;让我们为 D3Dframe 示例调用“公共接口”。这正是 D3D SDK 示例所做的。让我们看一看这些函数:

OneTimeSceneInit 允许示例完成一次性初始化任务,例如加载纹理和 x-文件,建立计算数值,生成程序的结构和类似的操作。通常,任何一次性的资源分配应该在这里完成。在每个应用程序执行周期中,OneTimeSceneInit 被调用一次。

InitDeviceObjects 给示例提供了初始化每个设备对象的机会。通常情况下,程序执行诸如将纹理位加载到设备表面、设置矩阵、设置光源以及填充顶点缓冲区等等操作。在初始化第一个设备以及每当设备有变化时,调用 InitDeviceObjects

DeleteDeviceObjects 包含示例用于删除在 InitDeviceObjects 中创建的对象的代码。这两个函数是成对匹配的;一定要使您的设备专用的资源分配与删除匹配,否则每当设备改变时将泄漏内存。每当设备被取消时调用 DeleteDeviceObjects

Render 是所有展现发生的地方。在这儿,清除框架缓冲区并调用 BeginScene/EndScene 对之间所有的展现代码。每帧调用 Render 一次。

FrameMove 提供了收集每帧动画任务的简便方法。更新矩阵,纹理调整,对象调整和其他随时间变化的动作都发生在 FrameMove。每帧调用 FrameMove 一次。

RestoreSurfaces 使示例测试丢失的表面并重新恢复它们,使应用程序可以继续执行。在表面因应用程序切换而丢失时,调用 RestoreSurfaces

FinalCleanupOneTimeSceneInit 配对,并提供一次取消一个应用程序的对象的方法。一定要使清除代码与初始化代码匹配,避免内存泄漏。

D3DFrame 内部

一旦掌握了应用函数的用途后,就该看一看什么东西会使它们准确运行。除了公共接口以外,类 CD3DApplication 有两个值得检查的部分。首先,类 CD3DApplication 包含下列成员函数:

  HRESULT Initialize3DEnvironment();
  HRESULT Change3DEnvironment();
  HRESULT Render3DEnvironment();
  VOID  Cleanup3DEnvironment();

这些函数使用了公共接口和类 CD3DFramework7,来处理 D3D 应用程序的许多工作。

Initialize3DEnvironment 调用 CD3DFramework7->Initialize 来初始化示例框架,然后调用公共接口 InitDeviceObjects 来初始化设备专用对象。构造该代码用于处理在初始化过程中可能发生的错误。

Change3DEnvironment 为应用程序处理驱动程序、设备和/或模式的改变。它调用公共接口 DeleteDeviceObjects 释放旧设备的对象。然后建立新的设备状态,并调用 Initialize3DEnvironment 以创建新设备的环境。

Render3DEnvironment 完成五个重要的任务。首先,它检查合作状态以保证它可以继续展现。接着,它为随时间变化的动画调用公共接口 FrameMove。然后调用公共接口 Render 以完成绘制。如果启动了统计,则进行绘制。最后使用 CD3DFramework7->ShowFrame 显示帧。如果显示帧导致表面丢失情况,则框架和公共接口 RestoreSurfaces 都将被调用。

Cleanup3DEnvironment 调用 DeleteDeviceObjectsFinalCleanup 为应用程序结束做准备。

我们不要忘了 D3D 应用程序实际上是 Windows 应用程序。Win32 应用程序的内容核心隐藏在成员函数中:

  virtual HRESULT Create( HINSTANCE, TCHAR* );
  virtual INT   Run();
  virtual LRESULT MsgProc( HWND hWnd, UINT uMsg, 
   WPARAM wParam, LPARAM lParam );

在往下深入讲解之前,让我们看一看当使用 CD3DApplicationWinMain 变为什么。在本实例中,WinMain 是这样结束的:

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
{
  CMyD3DApplication d3dApp;

  if( FAILED( d3dApp.Create( hInst, strCmdLine ) ) )
    return 0;

  return d3dApp.Run();

}

这是非常简单的 WinMain。并且隐藏 CreateRun 的动机是明确的,因为它们是 WinmainMsgProc 是 Windows 消息处理程序。在内部,它们都使用 xxx3DEnvironment 函数和类 CD3DFramework7 来管理 D3D 应用程序以及 Windows 应用程序。

Create 使用模块 D3DEnum 提供的对枚举驱动程序、设备的支持,以及当前硬件的模式支持;它还选择一个默认值。然后调用公共接口 OneTimeSceneInit。一旦创建了用户对象,新的 CD3DFramework7 对象将实例化。需要注意一般的 Win32 细节,注册窗口类,然后创建窗口。最后,调用 Initialize3DEvironment 创建新设备的环境。

Run 完成两个重要的任务:Windows 消息循环处理和调用 Render3DEnvironmentRun 也为 WM_QUIT 消息和退出做准备。

MsgProc 是 Windows 消息的消息处理程序。它依赖并使用 xxx3DEvironment 函数和 CD3DFramework7 对象管理 Win32 和 D3D 应用程序。

现在,我已经大体介绍了 CD3DApplication 类的工作方式,以及它如何使用 CD3DFramework7 类,下面再深入研究 CD3DFramework7 和它的内部。

在类 CD3DFramework7 中主要的繁重任务是由下面两组成员函数完成的:

  // 创建框架 
  HRESULT Initialize( HWND hWnd,GUID* pDriverGUID, 
      GUID* pDeviceGUID, DDSURFACEDESC2* pddsd, 
      DWORD dwFlags );
  HRESULT DestroyObjects();

  // 框架类的内部函数
  HRESULT CreateZBuffer( GUID* );
  HRESULT CreateFullscreenBuffers( DDSURFACEDESC2* );
  HRESULT CreateWindowedBuffers();
  HRESULT CreateDirectDraw( GUID*, DWORD );
  HRESULT CreateDirect3D( GUID* );
  HRESULT CreateEnvironment( GUID*, GUID*, DDSURFACEDESC2*,
DWORD );

让我们先看第一组,InitializeDestroyObjects

Initialize 是创建框架内部对象的公共函数。它在内部建立一些状态,然后依赖 CreateEnvironment 去完成大部分艰难的工作。

DestroyObjects 仅仅释放 Initialize 中创建的所有 DirectX COM 对象。

CreateEnvironment 创建框架的内部对象。它决定设备的内存类型,然后调用 CreateDirectDraw。一旦它们不正常,CreateFullscreenBuffersCreateWindowedBuffers 就会创建前台和后端缓冲区。最后,CreateDirect3D 创建 3-D 设备,并且 CreateZBuffer 创建并连接 Z-缓冲区。

鉴于理解 D3DFrame 的目的,我们不需要了解 CreateDirectDrawCreateFullscreenBuffersCreateWindowedBuffersCreateDirect3DCreateZBuffer 内部的详细实现。它们是直接相关的 — 这一句就够了。而对于那些想了解如何精确实现的人,源代码正等待他们去研究。

CD3Dframework7 中值得一提的最后几组函数提供了另外一些对展现的支持。它们是:

  // 帮助展现的函数
  HRESULT RestoreSurfaces();
  HRESULT ShowFrame();
  HRESULT FlipToGDISurface( BOOL bDrawFrame = FALSE ); 
  VOID  Move( INT x, INT y ); 

对于这些内容,我只是粗略带过,因为它们在先前检验的函数中使用,并且它们的用法可以轻松地从上下文导出。

要创建新的示例,现在只须实现一个类,诸如,下面所示的 CMyD3Dapplication

class CMyD3DApplication : public CD3DApplication
{
protected:
  HRESULT OneTimeSceneInit();
  HRESULT InitDeviceObjects();
  HRESULT DeleteDeviceObjects();
  HRESULT Render();
  HRESULT FrameMove( FLOAT fTimeKey );
  HRESULT FinalCleanup();

  static HRESULT ConfirmDevice( DDCAPS*, D3DDEVICEDESC7* );
public:
  CMyD3DApplication();
}

完成之后,您就拥有一个有效的 D3DFrame 示例了。可以看见它在 D3D SDK 示例中反复出现。或者等下一个专栏。仅提供空实现功能将导致空的 D3D 应用程序。我已经完成它,将作为以后专栏的基础,这里我也提供了源代码。运行在窗口中的空 D3D 应用程序,如图 2 所示。


图 2. D3DFrameApp 示例

结束语

作为下面几部分的难题,请参阅关于下列内容的四篇 DirectX 7.0 Direct3D 文章:

  1. 虚拟光
  2. 两篇关于 Alpha 混合物的文章:
    1. 顶点中的 Alpha
    2. 纹理中的 Alpha
  3. DotProduct3 展现

“虚拟光”一文将说明如何操纵 Direct3D 光,以避开大多数硬件施加的八光限制。Alpha 混合物文章将说明两种不同的透明技术:顶点中的透明和纹理中的透明。DotProduct3 文章将讨论非常好的 DOTPRODUCT3 操作符及其用途(提示,可将 DOTPRODUCT3 视为广义的从矢量到像素的查找函数)。每篇文章都包含一个基于 D3DFrame 的示例。

欢迎您提出反馈!欢迎您通过下面的地址,将您的评论、问题、主题思想或者您对本专栏涉及的主题的不同意见告诉我。不过,请不要期待单独回复或发给我请求支持的问题。请记住,Microsoft 有一个活动邮件的列表,DirectXDev,供志趣相投的开发者作为论坛来共享信息。Web 界面位于 http://DISCUSS.MICROSOFT.COM/archives/DIRECTXDEV.html(英文)。提问之前,请阅读 FAQ,它位于 http://msdn.microsoft.com/library/techart/dxfaq2.htm(英文)。

0 0

相关博文

我的热门文章

img
取 消
img