完全剖析direct3d------第一章 Direct X基础

来源:互联网 发布:王侯将相宁有种乎出处 编辑:程序博客网 时间:2024/05/19 05:39

1. Direct X基础

Microsoft DirectX应用程序接口(API)发展的目的,是为了要提供一组接口,让执行Microsoft Windows操作系统的计算机能很有效地掌控多媒体硬件。DirectX让程序设计人员可以使用非常接近硬件内部处理指令和数据架构的方法,却不需要太过低阶到必须针对不同装置撰写不同的原始码。程序设计人员只要撰写非装置相依性的原始码,就可以开发出永远最佳化的软件-即使是使用者为了改善系统效能而增加了更好的3D图形加速卡、声卡、输入装置等等。

DirectX的设计理念是要让开发人员拥有一个类似MS-DOS效能的环境,而且速度远快于Windows-based程序代码,因为早期的Windows多媒体API包含了不必要的系统负担。然而,由于支持了所有可运用的硬件特性,DirectX的程序代码执行速度甚至比多数的MS-DOS应用程序还快。DirectX API是架构在一种可以忽略硬件装置依赖性的硬件抽象层(hardware abstraction layer HAL)上。也因为DirectX是针对未来的扩充性所设计,所以很多现有硬件还无法支持DirectX所定义的许多硬件加速支持特性。这种特性会由硬件仿真层(hardware emulation layerHEL)来模拟,如果当HEL无法支持时则予忽略。当某种能加速DirectX功能(例如进阶纹理)的装置出现时,您便可以用它来取代旧的装置,而您所开发的前瞻性软件就会立刻运用新装置所支持的加速特性。

当您针对某种装置建立一个DirectX对象时,DirectX会自动向硬件(也就是HAL)查询关于这个装置的信息。这些信息会填入一个叫特性位(cap bits)的表格中(特性位是功能位(capability bits)的缩写)。特性位中的信息可以用来决定某种特定动作是由硬件本身能执行或是要用HEL去模拟。

即使Microsoft提供了特性位让您可以决定硬件(HAL)中提供哪些特性或是哪些必须经由软件(HEL)仿真,您还是应该定义出一套能接受的最低系统组态,并且让您的程序代码最佳化,让程序在这种最低系统上也能尽可能有效率地执行。换句话说,非万不得已不去使用HEL,同时您应该在HEL的影响性最小的前提下去开发应用程序。如此就能利用目标系统上更强大的硬件支持的进阶功能,而不会迁就这种最低配备。这样就能在游戏中让那些拥有功能强大的系统玩家提供许多加强的功能,如进阶贴图、较复杂的多边形,或是动态光源等。不过您得确定在设计上必须尽可能地充分运用进阶的特性。记住,硬件的处理能力进步越来越快,同时游戏的狂热分子也会不停地更新他们的系统,如果您在设计软件时不向前思考,势必将失去可观的销售量。

DirectX API
 

DirectX API包含了数种API,这些API是设计来合并使用并协助您开发3D多媒体游戏及仿真(以及其它许多的非3D应用程序)。它提供功能的函式库来执行2D3D的成像;标准及3D音效、音乐、支持键盘、游戏杆,及其它的输入装置(包括力回馈硬件)、网络游戏。这些接口相互结合可以提供一组强大并整合的指令函式库,让您可以有效地创造出优越的游戏及仿真程序。

目前在DirectX中已提供的APIDirectDrawDirect3DDirectMusicDirectSoundDirectPlayDirectInput,以及DirectSetup。在本书及随书光盘的程序代码中,将会告诉您如何去整合其中的数种APIDirectDrawDirect3DDirectSoundDirectPlay,以及DirectInput-来完成一个焦点放在第一人称角度的3D游戏类应用程序。

DirectDraw
 

DirectDraw API提供显示装置的处理、点阵数据的控制、屏幕外内存,及快速存取其它硬件特性(如图块搬移、切换页)。它同时也是Direct3D的基本架构。 第三章 ,本书还会提到所有关于要建立一个Direct3D的应用程序时所必须了解的DirectDraw特性。您可以运用这里的原始码作为您所开发的3D游戏架构。如果您愿意,您也可以运用DirectDraw本身来开发2D游戏;然而这里不会描述如何用DirectDraw去开发2D游戏,因为本书的重点是在3D游戏上。

Direct3D
 

Direct3D是本书的重点,它是一种可以用3D图形及利用3D指令的硬件加速特性优点来撰写程序的API。几乎所有市售的绘图卡都支持3D加速,这对个人计算机上的3D绘图应用的革新来说是极其重大的。几乎所有Windows上的3D游戏都是用Direct3D所完成的。

Direct3D刚出现时,它提供二种API:立即模式(Immediate Mode)和Retained模式。立即模式(命名由来是因为对象的绘制会在指令启动时立即发生)较不易使用,但却是弹性较大,较低阶的API,在开发游戏时也会执行得较有效率。Retained模式(命名由来是因为API会先存入影像数据库中再一次全部绘制成像)则是架构在立即模式的上一层,用来提供附加的功能,如贴图管理、对象档案加载、框架阶层,及动画。Retained模式比立即模式易学易用,但是程序设计师需求的还是立即模式所能提供的效率及弹性。Retained模式API的开发在DirectX 6时已经中止,Direct3D则把重心放在改善立即模式API的威力及易用程度上。Retained模式并不支持重要的新技术,如多重贴图(multitexturing)、凹凸对映(bump mapping),或硬件转换及打光(lighting),所有未来的3D程序都应该用立即模式开发,本书也是完全针对立即模式的API而来,一旦您读完了这本书,您将可以对立即模式的细微差异有充分的了解,而且您可以知道如何整合其它的DirectX来建立强大的3D游戏。

DirectMusic
 

这种DirectXAPI是用来处理message-based的音乐数据,并透过硬件或软件合成器来转换成波形取样数据(wave samples)。预设的软件作法是采用Microsoft Software Synthesizer来建立波形取样数据,并串流给DirectSound。乐器的声音则是根据Downloadable SoundsDLS)标准从取样资料中合成而来。DirectMusic也包含一个组合引擎,可以依据所提供的特定规则立刻组合出音乐。

DirectSound
 

这类API提供了有效率的立体声及3D音效处理,包含内存管理及硬件音效混音。DirectSound完全使用了系统硬件的优点。将3D的音效整合入所开发的游戏及仿真程序是一个好的想法,因为如此将增加应用程序的真实度。听到声音来自您的左方、右方、上方-也就是四面八方-可以使得环境更加吸引人,尤其是当您正利用DirectPlay与对手进行对战时更是如此。

DirectPlay
 

DirectPlay可以管理多人游戏并且启动与传输无关的联机动作及讯息服务。游戏玩家老早就希望那些优良的第一人称游戏可以支持网络联机、调制解调器联机,甚至二者同时。目前有许多最受欢迎的游戏都已提供这些功能,玩家也了解到多人游戏(head-to-head play)远比单人或和计算机对玩有趣的多。(许多企业也因此得到了教训-在他们的网络变得拥塞,以至于必须禁止员工在办公室中玩这类游戏之后!)我建议您,如果想在游戏市场上获得成功,记得在任何第一人称游戏中加入网络联机游戏功能。在第十五章,您会看到所有关于游戏如何透过网络、因特网或调制解调器来连到主机的DirectPlay程序代码。

DirectInput
 

DirectX API针对几乎所有的输入装置提供了低阶的输入功能支持。DirectX 7针对不同的装置提供了力回馈(force-feedback)功能的支持,包括方向盘及游戏杆。力回馈装置可以仿真某些感知状况,如路面突起的后座力、飞行时的风速切力,以及船航行时的水面波动。

DirectSetup
 

这类的API负责处理DirectX的自动安装。大多数的系统已经安装了DirectX,但是对还没安装的系统而言,DirectSetup提供了一套自动的步骤来安装应用程序需要的DirectX执行时期组件。因为Microsoft同意免费提供DirectX,开发人员可以在软件中加入判断,当DirectX不存在或版本过旧时重新予以安装。MicrosoftDirectX 7 SDKDXF/redist目录下提供了DirectX执行时期组件,本书的随书光盘也有附上。记住,DirectX执行时期组件的用途必须受到DXF/redist/license/DirectX end user eula.txt档案中的End-User License Agreement所限制。

Direct3DDirectX API中的角色
 

Direct3D最早出现在1996年的DirectX 2.0中,在它出现后没多久,Direct3D就变成市场上最受欢迎的3D API。许多的游戏及仿真程序都运用它,并且几乎所有的3D加速绘图卡都支持它。目前开发的硬件加速功能可以在绘图卡上做到每秒产生数百万个多边形的效率,价钱约在美金100200元之间。只不过在不久前,这种速度只能在价值数千美元的硬件上才做的到。像这样结合了Direct3D的加速卡已经大幅地增加速度并显著的降低了3D应用的成本。高效率不再意味着必须付出高成本。

如同所有的DirectX API一样,Direct3D不仅仅是用来提供开发人员一个共同的API作为游戏开发时的未来延伸之用,同时也完全兼顾了向后的兼容性。所有运用早期版本的DirectX所开发的游戏也都可以在未来的DirectX版本下正常执行。Direct3D应用程序搭配图形加速硬件时,在立即模式和Retained模式下的表现类似。我们之前提过,他们都是尽可能地透过HAL来使用硬件 -要不然便使用HEL。因为Direct3D扮演着DirectDraw对象接口的角色, Microsoft通常把这种HAL称为DirectDraw/Direct3D HAL。图1-1说明了Direct3D如何在Microsoft win32环境以及可用的硬件加速卡下运作。


 

1-1 Direct3DDirectX环境和系统的关系

在使用DirectX API前必须知道的事
 

要有效地使用DirectX API,您必须了解几个关键概念:如何使用组件对象模型(Component Object ModelCOM)、使用AddRefRelease方法、知道用CC++呼叫DirectX方法的差异,以及管理DirectX传回值。我们会在本章的下半段提到这些主题。

何谓COM物件?
 

一个COM接口就类似于在C++中的抽象类别(abstract class)。就如同C++抽象程序代码类别并不实际与程序代码结合一样,COM接口主要是用来描述一些符号和语意,但不包括实体部分。COM接口及单纯的虚拟函式使用了一个称为vtable的装置。这个装置中记录着用来开发接口的函式(也称为方法)的地址。要产生一个能使用COM接口方法的对象,您可以呼叫QueryInterface方法来确保对象的接口存在并且取得连到该接口的指针。您可以利用呼叫QueryInterface方法所传回的vtable的指针再去呼叫对象产生接口的方法。

程序设计人员会透过数种COM对象来引用DirectX的特性。每一种对象的接口都代表了某种装置,例如IDirectDraw7则是直接从IUnknown COM接口衍生而来。这些对象是利用每个对象的动态连结函式库(DLL)中的函式所产生的。在某些情况下,这类基本层级对象的接口是透过某个函式所取得,例如DirectDrawCreateEx。在其它情况下,标准的COM CoCreateInstance函式可用来将对象初始化并传回一个连到它的界面指针。DirectX对象模型会针对每个装置提供一个主要的对象,其它的支持对象则从这个主要对象中产生。对DirectDraw来说,这个主要对象就是显示处理器。您可以用这个对象来找出硬件装置上所提供的功能,例如色彩数及可支持的屏幕尺寸,您也可以用它来建立其它对象并且取得他们的接口,例如IDirectDrawPalette对象,就代表某个硬件色盘,而IDirectDrawSurface7对象,则表示显示内存。

COM对象的功能会在建立提供新特性的新接口时同步更新,而不需要改变现存接口中的方法。COM的概念是为了确保所有为了COM基础的应用程序所开发的软件和对象在未来的新版软件中也能完全兼容。为了取得新的DirectX界面所提供的特性,您必须呼叫对象的QueryInterface方法,使用您所要存取接口的全域性唯一识别码(globally unique identifierGUID),例如IID_IDirectDraw7。如果您的应用程序不会用到新版本接口中提供的新特性时,也就不需要存取这些新版接口。由于DirectX运用了接口设计中的COM样式。举例来说,DirectDraw提供了五种接口来存取一个DirectDrawSurface对象: IDirectDrawSurfaceIDirectDrawSurface2IDirectDrawSurface3 IDirectDrawSurface4IDirectDrawSurface7。即使DirectX最后也提供了IDirectDrawSurface29,您可以确信您的程序中所呼叫的IDirectDrawSurface7仍然可用。

AddRefRelease方法的用途为何?
 

除了QueryInterface方法之外,所有的COM接口也提供了二种其它的方法:AddRefRelease。这些方法是用来记忆对象的参照数量。参照数的计算是一种让多段程序代码可以同时使用某种资源而且仍然能正常的处理释放动作的技术。AddRef方法会将对象的参照数目加一,而Release则会将物件的参照数目减一。当函式传回一个对象的接口指针时(例如,当您建立一个对象的实体),这个函式必须透过指标呼叫AddRef来增加参照数。您必须透过相同的指标来维持呼叫Release和呼叫AddRef的一对一关系。您必须在结束指标前透过指标呼叫Release。当对象的参照数变成0时,对象会被释放而且所有参照到它的接口也会失效。

因此,当QueryInterface传回一个连结接口的指针时,它会呼叫AddRef来增加参照数目。也因为这样,您必须记得在释放该接口的指针前呼叫Release以扣除参照数目。当您呼叫任何会增加连结接口指针的DirectX API时,也要遵循该规则,例如IDirectDrawSurface7::GetAttachedSurface

CC++呼叫DirectX方法的差异
 

DirectX可以用在CC++Microsoft Visual Basic所开发的应用程序上。在本书中,我们会把焦点放在C++上。当您要决定以CC++来开发程序代码时,您必须考虑一些原则。

DirectX方法来说,第一个参数是连结接口或类别(也就是对象方法的引用)的指标,这基本上等于C++中的this参数。COM对象和C++对象的二进制原始码是兼容的,因此编译器可以用同样的方式来处理COM对象和C++抽象类别。

C语言中,您会透过传递this指标作为方法的第一个参数来呼叫,并且透过一个连结接口对象的vtable指针来参照接口的方法。比方说,我们用以下的C语言来建立一个3D应用程序的绘图页。

hr =lpdd->lpVtbl->CreateSurface(lpdd,&ddsd,&lpdds,NULL)

第一个参数(lpdd)是一个连到用来控制显示的DirectDraw对象的指针。第二个参数(ddsd)是一个DDSURFACEDESC2结构,包含了我们欲建立绘图页的相关信息。第三个参数(lpdds)则是一个地址,内容是所建立的IDirectDrawSurface7对象连结的指针。最后一个参数是NULL,因为它是个针对COM聚集特点未来兼容性的变量;如果您设定为非NULL值,就会发生错误。

C++语言中,lpVtbl指针会被隐含性地去除参照,而this参数则是隐含性的传入。因此,呼叫方式会改成:

hr =lpdd->CreateSurface(&ddsd,&lpdds,NULL)

除了这些考虑外,这二种语言的撰写方式应该非常类似。我较喜欢C++的理由是因为在我的应用程序中用到了对象导向的特性。我认为此特性可以将对象定义的适用范围涵盖贴图及文字,是最符合逻辑的作法,尤其是当我要写程序来仿真第一人称游戏所需要的真实世界的对象时。

回传值(Return Codes
 

当您呼叫某个DirectX方法时,任何用DirectX所产生的原始码都应该能处理接收到的回传值。所谓回传值是指当方法结束时传回的值,用来表示呼叫的成功与否。所有从DirectX方法传回的值都是HRESULT数据型态。所谓HRESULT是一个32位数字,代表二种意义:(1)呼叫成功或失败,(2)成功或失败的原因。SUCCEEDED()FAILED()的宏程序会把HRESULT当作参数,并且在它遇到成功代码或失败代码时回传。(SUCCEEDED()!FAILED()同义,所以您可以选择任一种表示方式来让程序代码看来更为简洁)。有时候程序设计师会想以比较HRESULTS_OK的方式来检查有无错误。这是一个不好的作法,因为某些函式会以S_OK以外的值来代表成功。(例如,某函式可能传回S_FALSE来表示什么也没发生,因为根本没必要去作任何事)。另一方面,只要您不去忽略了其它种类的失败, 采用比较HRESULT和某个特定的错误代码并且相对地响应的作法则是可接受的。

DirectX函式可以因为不同的原因而传回失败值。在开发新程序时常见的失败原因是发生在您传给函式一个不合法的输入值,或是呼叫函式去作用某个还没初始化的对象时。当您碰到这类失败时,处理程序响应的最好作法是(假设您还没结束该程序)暂停程序,并且将档案、行数、及错误代码作成报告,以便于您修正错误。在某些情况下,DirectX会传回失败代码来表示它无法执行某个指令,但您可以用别的方式来取代。举例来说,如果您试着在影像内存中建立一个绘图页,但DirectDraw传回DDERR_OUTOFVIDEOMEMORY,您可以改成在系统内存中建立该绘图页。有些失败是代表某种资源正忙碌中,那么您可以稍候再执行该指令,然而,大多数的情况下,DirectX的失败表示发生了不可预期的事件(例如内存用完了),或者是某个该有的指令并未执行(例如当您正需要某个特性时,它却无法运用)。在这些情况下,您必须让程序尽可能地解译错误代码的含意,并将状况以简单口语的方式回报给使用者,并且结束程序。

因为每个DirectX函式都会传回HRESULT,您必须很努力地将您的程序代码结构化,来检查并处理所有可能的错误。这项作法在开始时很难让人接受,但很快的就会变得理所当然。这种作法的结果是您的程序会非常不容易碰到毫无预期的当机状况。

Direct3D程序框架
 

DirectX Software Developer's KitSDK)中,DirectX提供了一些称为Direct3D程序框架(Direct3D Framework)的程序代码。这类程序代码并非Direct3D API本身的一部分,但却是辅助用的程序代码,用来执行一些常用的作业,如初始化、贴图管理、向量和矩阵数学,及模式切换。这类程序代码的用途是要为SDK中大部分的Direct3D范例程序提供一个共同的基础点,您可以自行选择是否在自己写的程序中引用。我认为您会发现:使用Direct3D程序框架可以省下一堆程序开发的工夫。本书中拿来当范例介绍的游戏RoadRage,就是以Direct3D程序框架为基础所完成的。而且当我在介绍Direct3D程序开发的几个重点时,也会让您看到许多关于它的程序。CD3DFramework7CD3DApplication类别即是Direct3D程序框架的一部分,其它以D3DEnumD3DUtilD3DMathD3DTextr开头的函式也是。记住您只要引用您需要的程序框架部分就好。比方说,您可以撰写您自己的贴图管理程序,而仍然引用Direct3D程序框架的初始化和工具函式。

结论
 

本章涵盖了所有关于DirectX和设计理念的基本概念。它描述了DirectX技术如何提供一个简单的方法给开发人员,来取得多媒体硬件的进阶特性,以及这种技术如何让开发人员运用与装置无关的程序代码来建立应用程序。您也看到了Direct3D正是构成DirectX的数种API之一(本章大致说明了这些API),而且您也学到了如何用COM模型来实作所有的DirectX 既然您已经了解了DirectXDirect3D的基本观念,让我们进一步去看看如何用Direct3D立即模式的特点来建立很棒的DirectX应用程序!

原创粉丝点击