DirectX5.0最新游戏编程指南
DirectDraw篇
DirectDraw是DirectX SDK的主要部分之一,它允许你直接对显示内存操作,支持硬件位块传输、硬件覆盖、表面翻转,并且保持同目前的基于Windows的应用程序和驱动程序兼容。
DirectDraw是一种软件接口,它除了能直接对显示设备存取外,还保持同Windows图形设备接口GDI(Graphics Device Interface)兼容。对于图形来说,它并不是一种高级应用程序接口。DirectDraw提供了一种设备无关性的方法,使得基于Windows的应用软件和游戏(例如三维图形软件包和数字视频游戏)能直接获取显示设备的特性。
DirectDraw能工作于各种各样的显示设备,从简单的SVGA显示器到能够提供剪切、拉伸和非RGB格式支持的高级显示设备。DirectDraw接口使得你的应用程序能仿真基本硬件的性能并使用硬件加速特性;硬件不提供的特性将由DirectX来仿真。DirectDraw提供的对显示内存的设备无关性的访问能使你很容易地管理显示内存。你的应用程序只需要识别一些基本的设备属性,它们都是标准的硬件应用,例如RGB和YUV格式的颜色。你不需要调用特殊的过程来使用位块传输或操纵调色板寄存器。使用DirectDraw,你可以很容易地操作显示内存,充
分利用不同类型显示设备的位块传输和颜色解压功能,而不需要依赖于特定的硬件。DirectDraw可以运行在Windows95/NT4.0和以后的版本中。
DirectDraw的硬件抽象层HAL(Hardware Abstraction Layer)提供了统一的接口,通过该接口,程序可以直接在显示内存或视频内存中工作,获取硬件的最佳性能。
DirectDraw对视频硬件的性能进行估计,只要可能就会使用硬件提供的特定性能。例如,如果视频卡支持位块传输,DirectDraw就会把位块传输委托给视频卡,CPU不参与位块传输的处理,这就大大提高了程序运行的性能。另外,DirectDraw提供了硬件仿真层HEL(Hardware Emulation Layer ),使得在某些硬件不存在时可以用软件仿真来支持本应该由这些硬件提供的特性。
DirectDraw运行在Windows 95上,能够利用32位内存的优越性和操作系统提供的“平坦”内存模型。DirectDraw将系统内存和视频内存作为大块存储而不是一小段一小段地使用。另外,DirectDraw还为 Windows图形程序员带来了许多强大的功能:
.DirectDraw使得在全屏模式下的应用多个后台缓冲区的页翻转变得容易
.支持窗口模式和全屏模式下的剪切功能
.支持三维 Z缓冲区
.支持Z方向的硬件辅助覆盖
.提供对图象拉伸的硬件的存取
.同时访问标准的和增强的显示设备内存区域
.动态调色板、独占式的硬件访问、分辨率切换等
将这些特性结合在一起,你就可以较容易地编制出性能超过基于GDI的标准Windows游戏甚至是MS-DOS下的游戏。
一、DirectDraw的基本图象概念
DirectDraw提供了一组不同于GDI的图形术语,因此,要用好DirectDraw,首先应该了解其中的有关概念。下面就对DirectDraw中重要的概念作一介绍。
1.1设备无关性位图DIB(Device-Independent Bitmap)
DirectX使用设备无关性位图DIB作为主要的图形文件格式。一个DIB文件主要保护了如下信息:图象的维数、使用的颜色数、描述颜色的值及描述每一个像素的数据。DIB文件还保护了较少用到的参数,象有关文件压缩的信息和图象的物理维数。DIB文件的扩展名一般是“.BMP”,有时也可能是“.DIB”。
因为DIB在Windows编程中的应用极其广泛,DirectX SDK已经包含了许多相关的函数。例如,DirectX SDK提供的ddutil.cpp文件中有一个函数,它将Win32和DirectX函数结合在一起,概念是把一个DIB文件装入DirectX表面,代码如下:
extern "C" IDirectDrawSurface * DDLoadBitmap(IDirectDraw *pdd,
LPCSTR szBitmap, int dx, int dy)
{
HBITMAP hbm;
BITMAP bm;
DDSURFACEDESC ddsd;
IDirectDrawSurface *pdds;
// This is the Win32 part.
// Try to load the bitmap as a resource, if that fails, try it as a file.
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), szBitmap, IMAGE_BITMAP, dx, dy, LR_CREATEDIBSECTION);
if (hbm == NULL)
hbm = (HBITMAP)LoadImage(NULL, szBitmap, IMAGE_BITMAP, dx, dy, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
if (hbm == NULL)
return NULL;
// Get the size of the bitmap.
GetObject(hbm, sizeof(bm), &bm);
// Now, return to DirectX function calls.
// Create a DirectDrawSurface for this bitmap.
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = bm.bmWidth;
ddsd.dwHeight = bm.bmHeight;
if (pdd->CreateSurface(&ddsd, &pdds, NULL) != DD_OK)
return NULL;
DDCopyBitmap(pdds, hbm, 0, 0, 0, 0);
DeleteObject(hbm);
return pdds;
}
1.2、绘图表面(Drawing Surface)
绘图表面接受视频数据并将其作为图象显示出来。在大多数的Windows应用程序中,你可以使用Win32的函数如GetDC来访问绘图表
面。GetDC获取设备上下文DC(Device Context),获得了设备上下文后,就可以重画表面了。然而,Win32的图象函数是由GDI提供的。
GDI是系统的一部分,它提供了抽象层,使得标准的Windows程序向表面绘图。
GDI的缺点是它并不是为高性能的多媒体软件设计的,它主要用于商业软件如字处理和电子表格软件。GDI提供了对系统内存中的视频缓冲区的访问,但不提供对视频内存的访问。尽管GDI对于大多数的商业软件非常合适,但对于多媒体应用程序和游戏软件则显得太慢了。
另一方面,DirectDraw 能提供表征真实视频内存的绘图表面,这就意味着当你使用DirectDraw 时,你能够直接向视频内存写数据,使得图象的显示速度足够快。这些表面表征为连续的内存块,使得寻址时更加容易。
1.3、位块传输Blit
Blit是“Bit Block Transfer”的简写,表示位块传输。它是将内存中一个地址的一块数据传送到另一个地址的一种方法。位块传输经常用于精灵动画中。你可以使用IDirectDrawSurface3::Blt 方法和IDirectDrawSurface3::BltFast方法来执行位块传输。
1.4、页翻转(Page Flipping)和后台缓冲(Back Buffering)
页翻转是多媒体、动画、游戏软件中的关键。软件页翻转是对卡通画家使图象运动的过程的模拟。例如,画家在一张纸上画了一个人物,然后将其置于下一帧的工作状态,对于每一帧,只很少地改变人物图象。当你快速翻转纸片时,连续的人物图象看起来就成了动画。
软件中的页翻转类似于上述的过程。首先,你建立了一系列DirectDraw表面,这些设计好的表面准备“翻转”到屏幕。第一个表面被看作是主表面(Primary Surface),在主表面后的所有表面都称为后台缓冲区。应用程序将数据写向后台缓冲区,然后翻转主表面,于是后台缓冲区就显示在片面上了。当系统正在显示图象时,程序就可以向后台缓冲区写数据,这个过程一直持续到动画的结束,它使你快速而高效地将离散的图象变成动画。 DirectDraw可以利用相对简单一些的双缓冲区(一个主表面和一个后台缓冲区),也可以使用较复杂的技术,加入其它的后台缓冲区。使得你能够容易地建立页翻转的程序。
1.5、矩形(Rectangle)
贯穿DirectDraw和Windows编程的一个最重要的概念是表面上的对象──有界矩形。一个有界矩形由两个点来确定,即左上角和右下角。当以位块传输的方式向屏幕写数据时,大多数的应用程序都使用RECT结构来传送有关有界矩形的信息。RECT结构的定义如下:
typedef struct tagRECT {
LONG left; // This is the top-left corner's X-coordinate.
LONG top; // The top-left corner's Y-coordinate.
LONG right; // The bottom-right corner's X-coordinate.
LONG bottom; // The bottom-right corner's Y-coordinate.
} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;
其中left和top成员变量是矩形左上角的X、Y坐标的值,right和bottom是右下角的X、Y的坐标值。
1.6、精灵(Sprite )
许多视频游戏都使用了精灵。从最基本的意义上来讲,一个精灵就是在屏幕上移动的图象。精灵画在一个表面上,覆盖在已有的背景上,合成后的图象被送到屏幕上显示出来。
1.6.1、透明位块传输(Transparent Blitting)和Color Key
精灵动画中的难点在于对非标准矩形的精灵的处理。因为位块传输函数是工作在矩形方式下,你的精灵也必须放在一个矩形之中,而不管它在屏幕上是否矩形。
精灵图象本身不是矩形的,但却包含在矩形区域中。当用位块传输将图象移动到目的地时,矩形区域中不属于精灵的像素就被“透明”处理。程序员选择任意一种颜色来创建精灵,这种颜色就被用来作为透明度“Color Key”。它是一种不常用的颜色,程序员仅仅用于表示透明度或特定的颜色范围。
使用IDirectDrawSurface3::SetColorKey方法,你可以为一个表面设定Color Key。设置了Color Key后,调用 IDirectDrawSurface3::BltFast方法来使用已设好的Color Key,而不管像素是否同该Color Key匹配。这种类型的Color Key是源 Color Key。源Color Key禁止“透明”的像素写到目的地,这样,原来的背景像素就保留下来了,使得精灵看起来就是非矩形的了并且精灵还可以在背景上移动。
另外,你还可以使用一个Color Key来影响目的表面(目的Color Key)。目的Color Key也是表面上的一种颜色,它用于指明像素是否可以被精灵所覆盖。
1.6.2、精灵和修补矩形(Sprite and Patch Rectangle)
为了产生精灵运动的效果,你在将精灵画到新的位置之前还必须从背景上擦除旧的位置上的精灵图象。当然,也可以重新调入整个背景再重画精灵,但这会大大降低动画的质量。事实上,你可以保留精灵矩形的上一次的轨迹,仅对该位置重画。这种方法称为“修补”(Patching)。
为了修补精灵旧的位置,可以利用原始背景图象(已经调入到一个屏外表面)的一个拷贝来重画这一位置。在这个过程中使用的是位块传输方法,在整个表面的循环中进行,需要耗用的处理时间很少。下面是这一过程的简单步骤:
(1). 设置精灵上一次位置的修补矩形
(2). 使用位块传输将屏外表面中的背景图象的主拷贝补到该位置
(3). 更新精灵是目的矩形,它反映了精灵新的位置
(4). 将精灵位块传输到背景上最新更新的矩形位置
(5).重复
将DirectDraw提供的强大的图象功能同直线式的C/C++程序结合起来,利用上面的步骤,马上就可以创建一个简单的精灵动画。
1.6.3、边界检查和碰撞检测(Bounds Checking and Hit Detection)
在精灵动画中,边界检查和碰撞检测是两个非常车间而又相当重要的任务。边界是限制精灵活动的范围,边界检查就是通过RECT结构来检查精灵的位置,判断精灵是否超出了限定的范围。
碰撞检测就是检查多个精灵是否占用了同一个位置。大部分的碰撞检测是检查各个精灵的矩形之间是否有所覆盖。