以文本方式查看主题

-  中文XML论坛 - 专业的XML技术讨论区  (http://bbs.xml.org.cn/index.asp)
--  『 C/C++编程思想 』  (http://bbs.xml.org.cn/list.asp?boardid=61)
----  初始化Direct3D  (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=69883)


--  作者:卷积内核
--  发布时间:11/27/2008 8:06:00 AM

--  初始化Direct3D
1.1 Direct3D概述
Direct3D是一种低层图形API,它能让我们利用3D硬件加速来渲染3D世界。我们可以把Direct3D看作是应用程序和图形设备之间的中介。例如通知图形设备清空屏幕,应用程序将调用Direct3D的IDirect3DDevice9::Clear方法。图1.1显示了应用程序、Direct3D和图形设备之间的关系。

按此在新窗口浏览图片

图1.1中Direct3D所表示的是Direct3D中已定义的,供程序员使用的Direct3D接口和函数的集合。这些接口和函数代表了当前版本的Direct3D所支持的全部特性。注意:仅仅因为Direct3D支持某种特性,并不意味着你所使用的图形硬件(显卡)也能支持它。

如图1.1所示,在Direct3D和图形设备之间有一层中介——叫做硬件抽象层(HAL,Hardware Abstraction Layer)。Direct3D不能直接作用于图形设备,因为现在市面上的显卡种类实在是太多了并且每种显卡都有不同的性能和处理事件的方式。例如,两种不同的显卡实现清屏的方式也可能是不同的。因此,Direct3D要求设备制造商实现HAL。HAL是一组指示设备执行某种操作的特殊设备代码的集合。用这种方法,Direct3D避免了必须去了解某个设备的特殊细节,使它能够独立于硬件设备。

设备制造商在HAL中实现他们的产品所支持的所有特性。HAL将不会实现那些Direct3D支持但硬件产品不支持的特性。调用一个HAL中没有实现的Direct3D的函数将会出错,除非它是顶点处理操作,因为这个功能可以由软件模拟来实现。因此当使用某些仅由市面上少数显卡所支持的高级特性时,必须检测一下设备是否支持。

1.1.1 REF设备
你也许想把一些你的设备不支持的Direct3D函数写入程序。为了达到这个目的,Direct3D提供了REF设备,它用软件模拟了所有的Direct3D API。这允许你写并测试那些你的显卡不支持的Direct3D特性的代码。懂得REF设备仅仅用于开发阶段,这是很重要的。它只会和DirectX SDK一起被装载,而不会发布给最终用户。另外,REF设备实在是太慢了,除了测试以外它没有任何利用价值。

1.1.2 D3DDEVTYPE
在代码中,我们用D3DDEVTYPE_HAL来定义HAL设备,它是D3DDEVTYPE枚举类型的一个成员。同样的,REF设备则由D3DDEVTYPE_REF来定义,它也属于D3DDEVTYPE枚举类型。记住这些类型很重要,因为在创建设备的时候我们需要指定我们将要使用的类型。

Defines device types.

typedef enum D3DDEVTYPE{    D3DDEVTYPE_HAL = 1,    D3DDEVTYPE_NULLREF = 4,    D3DDEVTYPE_REF = 2,    D3DDEVTYPE_SW = 3,    D3DDEVTYPE_FORCE_DWORD = 0xffffffff,} D3DDEVTYPE, *LPD3DDEVTYPE;
Constants
D3DDEVTYPE_HAL
Hardware rasterization. Shading is done with software, hardware, or mixed transform and lighting.
D3DDEVTYPE_NULLREF
Initialize Direct3D on a computer that has neither hardware nor reference rasterization available, and enable resources for 3D content creation. See Remarks.
D3DDEVTYPE_REF
Direct3D features are implemented in software; however, the reference rasterizer does make use of special CPU instructions whenever it can.
D3DDEVTYPE_SW
A pluggable software device that has been registered with IDirect3D9::RegisterSoftwareDevice.
D3DDEVTYPE_FORCE_DWORD
Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.
Remarks
All methods of the IDirect3D9 interface that take a D3DDEVTYPE device type will fail if D3DDEVTYPE_NULLREF is specified. To use these methods, substitute D3DDEVTYPE_REF in the method call.

A D3DDEVTYPE_REF device should be created in D3DPOOL_SCRATCH memory, unless vertex and index buffers are required. To support vertex and index buffers, create the device in D3DPOOL_SYSTEMMEM memory.

If D3dref9.dll is installed, Direct3D will use the reference rasterizer to create a D3DDEVTYPE_REF device type, even if D3DDEVTYPE_NULLREF is specified. If D3dref9.dll is not available and D3DDEVTYPE_NULLREF is specified, Direct3D will neither render nor present the scene.


--  作者:卷积内核
--  发布时间:11/27/2008 8:06:00 AM

--  
[B]
1.2 COM
组件对象模型(COM, Component Object Model)是一种能使DirectX独立于编程语言和具有向下兼容性的技术。我们通常把COM对象作为一个接口,你可以把它当作达到某种目的的C++类来使用它。当使用C++写DirectX程序的时候,COM的大部分细节对我们来说是透明。但是有一件事,我们必须知道,那就是我们通过某个特殊的COM接口的函数或指针获得了另一个COM接口指针,而不是通过C++的新关键字来创建它。当我们使用完某个接口后,调用它的Release方法比直接Delete它更好。COM对象具有它们自己的内存管理。

对COM来说还有很多细节可以了解,但是掌握这些细节对于我们有效的使用DirectX不是必须的。

注意:COM接口都具有前缀大写字母“I”,例如表示一个表面的COM接口叫做IDirect3DSurface9。

1.3 一些准备工作
Direct3D的初始化过程要求我们对图形学基础知识和Direct3D类型有一定了解。这里将介绍这些知识和类型,以确保以后能把焦点集中在讨论Direct3D的初始化上。

1.3.1 表面
表面是一个像素点阵,在Direct3D中主要用来存储2D图形数据。图1.2指明了表面的一些成分。由图可以看出表面数据就像一个矩阵,像素数据实际上存储在线性数组里面。

按此在新窗口浏览图片

表面的Width和Height是按像素计算的。Pitch以字节为单位。而且Pitch有可能比Width大且依赖于低层硬件,所以不能单纯的认为Pitch = Width * sizeof (pixelFormat)。

在代码中,我们可以使用IDirect3DSurface9接口来描述表面。这个接口提供若干方法来直接读写表面数据并且还有一个方法用来返回表面息。IDirect3DSurface9中最重要的方法是:

l         LockRect——使用这个方法,我们将获得一个指向表面内存的指针,然后,通过一系列指针运算,我们可以对表面上任一个像素点进行读、写操作。

Locks a rectangle on a surface.

HRESULT LockRect(  D3DLOCKED_RECT * pLockedRect,  CONST RECT * pRect,  DWORD Flags);
Parameters
pLockedRect
[out] Pointer to a D3DLOCKED_RECT structure that describes the locked region.
pRect
[in] Pointer to a rectangle to lock. Specified by a pointer to a RECT structure. Specifying NULL for this parameter expands the dirty region to cover the entire surface.
Flags
[in] Combination of zero or more locking flags that describe the type of lock to perform. For this method, the valid flags are:
D3DLOCK_DISCARD
D3DLOCK_DONOTWAIT
D3DLOCK_NO_DIRTY_UPDATE
D3DLOCK_NOSYSLOCK
D3DLOCK_READONLY
You may not specify a subrect when using D3DLOCK_DISCARD. For a description of the flags, see D3DLOCK.
Return Values
If the method succeeds, the return value is D3D_OK.

If the method fails, the return value can be D3DERR_INVALIDCALL or D3DERR_WASSTILLDRAWING.

Remarks
If the D3DLOCK_DONOTWAIT flag is specified and the driver cannot lock the surface immediately, IDirect3DSurface9::LockRect will return D3DERR_WASSTILLDRAWING so that an application can use the CPU cycles while waiting for the driver to lock the surface.

The only lockable format for a depth-stencil surface is D3DFMT_D16_LOCKABLE. See D3DFORMAT.

For performance reasons, dirty regions are recorded only for level zero of a texture. Dirty regions are automatically recorded when IDirect3DSurface9::LockRect is called without D3DLOCK_NO_DIRTY_UPDATE or D3DLOCK_READONLY. See IDirect3DDevice9::UpdateTexture for more information.

A multisample back buffer cannot be locked.

This method cannot retrieve data from a surface that is is contained by a texture resource created with D3DUSAGE_RENDERTARGET because such a texture must be assigned to D3DPOOL_DEFAULT memory and is therefore not lockable. In this case, use instead IDirect3DDevice9::GetRenderTargetData to copy texture data from device memory to system memory.

l         UnlockRect——当你调用了LockRect和完成了对表面内存的访问后,你必须调用这个方法给表面解锁。

Unlocks a rectangle on a surface.

HRESULT UnlockRect();
Parameters
None.

Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.

l         GetDesc——这个方法将通过填充D3DSURFACE_DESC结构来返回表面的描述信息。

D3DSURFACE_DESC
Describes a surface.

typedef struct D3DSURFACE_DESC {    D3DFORMAT Format;    D3DRESOURCETYPE Type;    DWORD Usage;    D3DPOOL Pool;    D3DMULTISAMPLE_TYPE MultiSampleType;    DWORD MultiSampleQuality;    UINT Width;    UINT Height;} D3DSURFACE_DESC, *LPD3DSURFACE_DESC;
Members
Format
Member of the D3DFORMAT enumerated type, describing the surface format.
Type
Member of the D3DRESOURCETYPE enumerated type, identifying this resource as a surface.
Usage
Either the D3DUSAGE_DEPTHSTENCIL or D3DUSAGE_RENDERTARGET values. For more information, see D3DUSAGE.
Pool
Member of the D3DPOOL enumerated type, specifying the class of memory allocated for this surface.
MultiSampleType
Member of the D3DMULTISAMPLE_TYPE enumerated type, specifying the levels of full-scene multisampling supported by the surface.
MultiSampleQuality
Quality level. The valid range is between zero and one less than the level returned by pQualityLevels used by IDirect3D9::CheckDeviceMultiSampleType. Passing a larger value returns the error, D3DERR_INVALIDCALL. The MultisampleQuality values of paired render targets, depth stencil surfaces and the MultiSample type must all match.
Width
Width of the surface, in pixels.
Height
Height of the surface, in pixels.
Retrieves a description of the surface.

HRESULT GetDesc(  D3DSURFACE_DESC * pDesc);
Parameters
pDesc
[out] Pointer to a D3DSURFACE_DESC structure, describing the surface.
Return Values
If the method succeeds, the return value is D3D_OK.

D3DERR_INVALIDCALL is returned if the argument is invalid.

最初锁定表面和改写每一像素看来稍微有点迷茫。下面的代码表示锁定表面并将每一像素染成红色:

// Assume _surface is a pointer to an IDirect3DSurface9 interface.
// Assumes a 32-bit pixel format for each pixel.

// Get the surface description.
D3DSURFACE_DESC surfaceDesc;
_surface->GetDesc(&surfaceDesc);

// Get a pointer to the surface pixel data.
D3DLOCKED RECT lockedRect;

_surface->LockRect(
      &lockedRect,// pointer to receive locked data
      0,          // lock entire surface
      0);         // no lock flags specified

// Iterate through each pixel in the surface and set it to red.
DWORD* imageData = (DWORD*)lockedRect.pBits;

for(int i = 0; i < surfaceDesc.Height; i++)
{
      for(int j = 0; j < surfaceDesc.Width; j++)
      {
            // index into texture, note we use the pitch and divide by
            // four since the pitch is given in bytes and there are 4 bytes per DWORD.
            int index = i * lockedRect.Pitch / 4 + j;

            imageData[index] = 0xffff0000; // red
      }
}

_surface->UnlockRect();

程序中D3DLOCKED_RECT结构的定义如下:

typedef struct _D3DLOCKED RECT {
    INT Pitch;   // the surface pitch
    void *pBits; // pointer to the start of the surface memory
} D3DLOCKED_RECT;
在这里有一些关于表面锁定代码的一些说明。32-bit像素格式这个设定很重要,我们把bits转换成DWORDs。这让我们能把每一个DWORD视为表示一个像素。
[/B]


--  作者:卷积内核
--  发布时间:11/27/2008 8:07:00 AM

--  
初始化Direct3D(2)
1.3.2 Multisampling
由于使用像素矩阵来表示图像,在显示时会出现锯齿状,Multisampling就是使其变得平滑的技术。它的一种最普通的用法即为——全屏抗锯齿(看图1.3)。
按此在新窗口浏览图片

D3DMULTISAMPLE_TYPE枚举类型使我们可以指定全屏抗锯齿的质量等级:

D3DMULTISAMPLE_NONE——不使用全屏抗锯齿。

D3DMULTISAMPLE_1_SAMPLE…D3DMULTISAPLE_16_SAMPLE——设定1~16级的等级。

Defines the levels of full-scene multisampling that the device can apply.

typedef enum D3DMULTISAMPLE_TYPE{    D3DMULTISAMPLE_NONE = 0,    D3DMULTISAMPLE_NONMASKABLE  = 1,    D3DMULTISAMPLE_2_SAMPLES = 2,    D3DMULTISAMPLE_3_SAMPLES = 3,    D3DMULTISAMPLE_4_SAMPLES = 4,    D3DMULTISAMPLE_5_SAMPLES = 5,    D3DMULTISAMPLE_6_SAMPLES = 6,    D3DMULTISAMPLE_7_SAMPLES = 7,    D3DMULTISAMPLE_8_SAMPLES = 8,    D3DMULTISAMPLE_9__SAMPLES = 9,    D3DMULTISAMPLE_10_SAMPLES = 10,    D3DMULTISAMPLE_11_SAMPLES = 11,    D3DMULTISAMPLE_12_SAMPLES = 12,    D3DMULTISAMPLE_13_SAMPLES = 13,    D3DMULTISAMPLE_14_SAMPLES = 14,    D3DMULTISAMPLE_15_SAMPLES = 15,    D3DMULTISAMPLE_16_SAMPLES = 16,    D3DMULTISAMPLE_FORCE_DWORD = 0xffffffff,} D3DMULTISAMPLE_TYPE, *LPD3DMULTISAMPLE_TYPE;
使用全屏抗锯齿的功能将大大的降低了程序运行速度。如果你实在很想使用它的话,要记住使用IDirect3D9::CheckDeviceMultisampleType来检测你的显卡是否支持。

1.3.3像素格式
当我们创建一个表面或纹理时,经常需要指定这些Direct3D资源的像素格式。它是由D3DFORMAT枚举类型的一个成员来定义的。这里例举一部分:

D3DFMT_R8G8B8——表示一个24位像素,从左开始,8位分配给红色,8位分配给绿色,8位分配给蓝色。

D3DFMT_X8R8G8B8——表示一个32位像素,从左开始,8位不用,8位分配给红色,8位分配给绿色,8位分配给蓝色。

D3DFMT_A8R8G8B8——表示一个32位像素,从左开始,8位为ALPHA通道,8位分配给红色,8位分配给绿色,8位分配给蓝色。

D3DFMT_A16B16G16R16F——表示一个64位浮点像素,从左开始,16位为ALPHA通道,16位分配给蓝色,16位分配给绿色,16位分配给红色。

D3DFMT_A32B32G32R32F——表示一个128位浮点像素,从左开始,32位为ALPHA通道,32位分配给蓝色,32位分配给绿色,32位分配给红色。

想了解全部的像素格式请查看SDK文档中的D3DFORMAT部分。

注意:这前三种格式(D3DFMT_R8G8B8、D3DFMT_X8R8G8B8、D3DFMT_A8R8G8B8)是最常用并为大部分显卡所支持。但浮点像素格式或其它一些类型的支持并不是很广泛,在使用它们前请先检测你的显卡,看是否支持。

1.3.4 内存池
表面和其它一些Direct3D资源被放在多种内存池中。内存池的种类由D3DPOOL枚举类型的一个成员来指定。可用到的内存池有下列几种:

D3DPOOL_DEFAULT——表示Direct3D将根据资源的类型和用途把它们放在最合适的地方。这有可能是显存、AGP内存或者系统内存中。值得注意的是,这种内存池中的资源必须要在IDirect3DDevice9::Reset被调用之前消毁掉,并且再次使用时必须重新初始化。

D3DPOOL_MANAGED——资源将由Direct3D管理并且按设备的需要来指定放在显存还是放在AGP内存中。当应用程序访问和改变资源时它先把这些资源拷贝到系统内存中,当需要时Direct3D会自动把它们拷贝到显存里。

D3DPOOL_SYSTEMMEM——指定资源放在系统内存中。

D3DPOOL_SCRATCH——指定资源放在系统内存中,它与D3DPOOL_SYSTEMMEM不同之处在于使用这些资源不必受图形设备的限制。因此,参数使图形设备不能访问该内存池的资源,但资源可以相互拷贝。



--  作者:卷积内核
--  发布时间:11/27/2008 8:09:00 AM

--  
AGP内存
AGP(Accelerate Graphical Port),加速图形接口。随着显示芯片的发展,PCI总线日益无法满足其需求。英特尔于1996年7月正式推出了AGP接口,它是一种显示卡专用的局部总线。严格的说,AGP不能称为总线,它与PCI总线不同,因为它是点对点连接,即连接控制芯片和AGP显示卡,但在习惯上我们依然称其为AGP总线。AGP接口是基于PCI 2.1 版规范并进行扩充修改而成,工作频率为66MHz。

  AGP总线直接与主板的北桥芯片相连,且通过该接口让显示芯片与系统主内存直接相连,避免了窄带宽的PCI总线形成的系统瓶颈,增加3D图形数据传输速度,同时在显存不足的情况下还可以调用系统主内存。所以它拥有很高的传输速率,这是PCI等总线无法与其相比拟的。

由于采用了数据读写的流水线操作减少了内存等待时间,数据传输速度有了很大提高;具有133MHz及更高的数据传输频率;地址信号与数据信号分离可提高随机内存访问的速度;采用并行操作允许在CPU访问系统RAM的同时AGP显示卡访问AGP内存;显示带宽也不与其它设备共享,从而进一步提高了系统性能。

  AGP标准在使用32位总线时,有66MHz和133MHz两种工作频率,最高数据传输率为266Mbps和533Mbps,而PCI总线理论上的最大传输率仅为133Mbps。目前最高规格的AGP 8X模式下,数据传输速度达到了2.1GB/s。

  AGP接口的发展经历了AGP1.0(AGP1X、AGP2X)、AGP2.0(AGP Pro、AGP4X)、AGP3.0(AGP8X)等阶段,其传输速度也从最早的AGP1X的266MB/S的带宽发展到了AGP8X的2.1GB/S。

AGP 1.0(AGP1X、AGP2X)
1996年7月AGP 1.0 图形标准问世,分为1X和2X两种模式,数据传输带宽分别达到了266MB/s和533MB/s。这种图形接口规范是在66MHz PCI2.1规范基础上经过扩充和加强而形成的,其工作频率为66MHz,工作电压为3.3v,在一段时间内基本满足了显示设备与系统交换数据的需要。这种规范中的AGP带宽很小,现在已经被淘汰了,只有在前几年的老主板上还见得到。

AGP2.0(AGP4X)
显示芯片的飞速发展,图形卡单位时间内所能处理的数据呈几何级数成倍增长,AGP 1.0 图形标准越来越难以满足技术的进步了,由此AGP 2.0便应运而生了。1998年5月份,AGP 2.0 规范正式发布,工作频率依然是66MHz,但工作电压降低到了1.5v,并且增加了4x模式,这样它的数据传输带宽达到了1066MB/sec,数据传输能力大大地增强了。

AGP Pro
AGP Pro接口与AGP 2.0同时推出,这是一种为了满足显示设备功耗日益加大的现实而研发的图形接口标准,应用该技术的图形接口主要的特点是比AGP 4x略长一些,其加长部分可容纳更多的电源引脚,使得这种接口可以驱动功耗更大(25-110w)或者处理能力更强大的AGP显卡。这种标准其实是专为高端图形工作站而设计的,完全兼容AGP 4x规范,使得AGP 4x的显卡也可以插在这种插槽中正常使用。AGP Pro在原有AGP插槽的两侧进行延伸,提供额外的电能。它是用来增强,而不是取代现有AGP插槽的功能。根据所能提供能量的不同,可以把AGP Pro细分为AGP Pro110和AGP Pro50。在某些高档台式机主板上也能见到AGP Pro插槽,例如华硕的许多主板。

AGP 3.0(AGP8X)
2000年8月,Intel推出AGP3.0规范,工作电压降到0.8V,并增加了8x模式,这样它的数据传输带宽达到了2133MB/sec,数据传输能力相对于AGP 4X成倍增长,能较好的满足当前显示设备的带宽需求。

AGP接口的模式传输方式
不同AGP接口的模式传输方式不同。1X模式的AGP,工作频率达到了PCI总线的两倍—66MHz,传输带宽理论上可达到266MB/s。AGP 2X工作频率同样为66MHz,但是它使用了正负沿(一个时钟周期的上升沿和下降沿)触发的工作方式,在这种触发方式中在一个时钟周期的上升沿和下降沿各传送一次数据,从而使得一个工作周期先后被触发两次,使传输带宽达到了加倍的目的,而这种触发信号的工作频率为133MHz,这样AGP 2X的传输带宽就达到了266MB/s×2(触发次数)=533MB/s的高度。AGP 4X仍使用了这种信号触发方式,只是利用两个触发信号在每个时钟周期的下降沿分别引起两次触发,从而达到了在一个时钟周期中触发4次的目的,这样在理论上它就可以达到266MB/s×2(单信号触发次数)×2(信号个数)=1066MB/s的带宽了。在AGP 8X规范中,这种触发模式仍然使用,只是触发信号的工作频率变成266MHz,两个信号触发点也变成了每个时钟周期的上升沿,单信号触发次数为4次,这样它在一个时钟周期所能传输的数据就从AGP4X的4倍变成了8倍,理论传输带宽将可达到266MB/s×4(单信号触发次数)×2(信号个数)=2133MB/s的高度了。

按此在新窗口浏览图片

目前常用的AGP接口为AGP4X、AGP PRO、AGP通用及AGP8X接口。需要说明的是由于AGP3.0显卡的额定电压为0.8—1.5V,因此不能把AGP8X的显卡插接到AGP1.0规格的插槽中。这就是说AGP8X规格与旧有的AGP1X/2X模式不兼容。而对于AGP4X系统,AGP8X显卡仍旧在其上工作,但仅会以AGP4X模式工作,无法发挥AGP8X的优势。

1.3.5 交换链和页面切换
Direct3D通常创建2~3个表面组成一个集合,即为交换链,通常由IDirect3DSwapChain接口来表示。我们不必去了解它更详细的细节。我们也很少去管理它,通常Direct3D会自己去管理。所以我们只要大概的了解一下它就可以了。

交换链以及页面切换技巧被用在使两帧动画之间过度更平滑。图1.4展示的是一个有两个绘制表面的交换链。

按此在新窗口浏览图片

如图1.4,在Front Buffer中的表面将用来在屏幕上显示。显示器不能即时显示Front Buffer中表示的图像;通常情况下,它是每六十分之一秒刷新显示一次,即刷新率为60赫兹。应用程序的帧率经常与监视器的刷新率不同步(比如应用程序的渲染帧速度可能比显示器的刷新速度快)。然而,我们不能在显示器显示完成当前帧之前就更新有下一帧动画的Front Buffer内容,但是我们又不想让程序停止渲染而去等待显示器显示。因此,我们渲染另一个屏幕表面Back Buffer。当监视器将Front Buffer显示出来后,Front Buffer就被放到交换链的末端,即变成图中的Back Buffer,而Back Buffer就会变成交换链中的Front Buffer。这个过程就叫做presenting。图1.5表示了交换的整个过程。

按此在新窗口浏览图片

因此,我们绘图代码的结构就会像下面这样:

1. Render to back buffer

2. Present the back buffer

3. Goto (1)

1.3.6 深度缓冲
深度缓冲也是一个表面,但它不是用来存储图像数据的,而是用来记录像素的深度信息。它将确定哪一个像素最后被绘制出来。所以,如果要绘制640*480分辨率的图片,那么就会有640*480个深度值。

按此在新窗口浏览图片

图1.6展示了一个简单的场景,在这个场景里,一个物体把将另一个物体的一部分遮住了。为了使Direct3D能确定物体的前后关系并正确的绘制出来,我们使用一种深度缓冲,又叫做z-buffering的技术。

深度缓冲为每一个像素计算深度值,并进行深度测试。通过深度测试,我们可以比较出哪个像素离照相机更近,并将它画出来。这样就可以只绘制最靠近照相机的像素,被遮住的像素就不会被画出来。

深度缓冲的格式决定着深度测试的精确性。一个24位的深度缓冲比16位的深度缓冲更精确。通常,应用程序在24位深度缓冲下就能工作的很好,但是Direct3D也同时支持32位的深度缓冲。

D3DFMT_D32——表示32位深度缓冲

D3DFMT_D24S8——表示24位深度缓冲并保留8位模版缓冲(stencil buffer)

D3DFMT_D24X8——表示24位深度缓冲

D3DFMT_D24X4S4——表示24位深度缓冲并保留4位模版缓冲

D3DFMT_D16——表示16位深度缓冲

1.3.7 顶点处理
顶点是3D图形学的基础,它能够通过两种不同的方法被处理,一种是软件方式(software vertex processing),一种是硬件方式(hardware vertex processing),前者总是被支持且永远可用,后者必须要显卡硬件支持顶点处理才可用。

使用硬件顶点处理总是首选,因为它比软件方式更快,而且不占用CPU资源,这意味CPU至少可以有更多的空闲时间进行别的计算。

注意:如果一块显卡支持硬件顶点处理的话,也就是说它也支持硬件几何转换和光源计算。

1.3.8 设备能力
Direct3D支持的每一项特性都对应于D3DCAPS9结构的一个数据成员。初始化一个D3DCAPS9实例应该以你的设备实际支持特性为基础。因此,在我们的应用程序里,我们能够通过检测D3DCAPS9结构中相对应的某一成员来检测设备是否支持这一特性。

下面将举例说明,假设我们想要检测显卡是否支持硬件顶点处理(换句话说,就是显卡是否支持硬件几何转换和光源计算)。通过查阅SDK中的D3DCAPS9结构,可以得知数据成员D3DCAPS9::DevCaps中的D3DDEVCAPS_HWTRANSFORMANDLIGHT位表示硬件是否支持硬件顶点处理即硬件几何变换和光源计算。程序如下:


bool supportsHardwareVertexProcessing;

// If the bit is "on" then that implies the hardware device supports it.
if( caps.DevCaps & D3DDEVCAPS HWTRANSFORMANDLIGHT )
{
      // Yes, the bit is on, so it is supported.
      supportsHardwareVertexProcessing = true;
}
else
{
      // No, the bit is off, so it is not supported.
      hardwareSupportsVertexProcessing =  false;
}

注意:DevCaps即为“device capabilities。


--  作者:卷积内核
--  发布时间:11/27/2008 8:27:00 AM

--  
1.4 初始化Direct3D
下面几点说明怎样初始化Direct3D。根据下边的步骤你能初始化Direct3D:

1.获得一个IDirect3D9接口指针。这个接口用于获得物理设备的信息和创建一个IDirect3DDevice9接口,它是一个代表我们显示3D图形的物理设备的C++对象。

2.检查设备能力(D3DCAPS9),搞清楚主显卡是否支持硬件顶点处理。我们需要知道假如它能支持,我们就能创建IDirect3DDevice9接口。

3.初始化一个D3DPRESENT_PARAMETERS结构实例,这个结构包含了许多数据成员允许我们指定将要创建的IDirect3DDevice9接口的特性。

4.创建一个基于已经初始化好的D3DPRESENT_PARAMETERS结构的IDirect3DDevice9对象。它是一个代表我们显示3D图形的物理设备的C++对象。

请注意,我们使用主显示设备绘制3D图形,如果你的机子只有一块显卡,那它就是主显示设备。如果你有多个显卡,那么你当前使用的显卡将会成为主显示设备(如:用来显示Windows桌面的显卡)。

1.4.1获得IDirect3D9接口Direct3D的初始化是从获得一个IDirect3D9接口指针开始的。使用一个专门的Direct3D函数来完成这个工作是非常容易的,代码如下:

IDirect3D9* _d3d9;

_d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

Direct3DCreate9的唯一一个参数总是D3D_SDK_VERSION,这可以保证应用程序通过正确的头文件被生成。如果函数调用失败,那么它将返回一个空指针。

IDirect3D9对象通常有两个用途:设备列举和创建IDirect3DDevice9对象。设备列举即为查明系统中显示设备的技术特性,显示模式、格式,以及其它每一种显卡各自支持的特性。创建代表物理设备的IDirect3DDevice9对象,我们需要利用这个物理设备的显示模式结构和格式来创建它。为了找到一个工作配置,我们必须使用IDirect3D9的列举方法。

然而,设备列举实在太慢了,为了使Direct3D运行得尽可能快,我们通常不使用这个测试,除了下一节所谈到的一项测试。为了安全跳过它,我们可以选择总是被所有显卡都支持的“安全”配置。

1.4.2 检测硬件顶点处理当我们创建一个IDirect3DDevice9对象来表示主显示设备时,必须要设定其顶点处理的类型。如果可以的话,当然要选用硬件顶点处理,但是由于并非所有显卡都支持硬件顶点处理,因此我们必须首先检查显卡是否支持。

首先我们要根据主显示设备的技术特性来初始化D3DCAPS9实例。可以使用如下方法:

HRESULT IDirect3D9::GetDeviceCaps(

       UINT Adapter,

       D3DDEVTYPE DeviceType,

       D3DCAPS9 *pCaps

);

Adapter——指定要获得哪个显示适配器的特性

DeviceType——指定设备类型(硬件设备(D3DDEVTYPE_HAL),软件设备(D3DDEVTYPE_REF))

PCaps——返回一个已初始化的D3DCAPS9结构

然后,我们就可以象1.3.8部分那样检测显卡的能力了。下面就是代码片段:

// Fill D3DCAPS9 structure with the capabilities of the primary display adapter.

D3DCAPS9 caps;

d3d9->GetDeviceCaps(
      D3DADAPTER_DEFAULT, // Denotes primary display adapter.
      deviceType, // Specifies the device type, usually D3DDEVTYPE HAL.
      &caps);     // Return filled D3DCAPS9 structure that contains
                  // the capabilities of the primary display adapter.

// Can we use hardware vertex processing?

int vp = 0;

if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
{
      // yes, save in 'vp' the fact that hardware vertex processing is supported.
      vp = D3DCREATE HARDWARE VERTEXPROCESSING;
}
else
{
      // no, save in 'vp' the fact that we must use software vertex processing.
      vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}

  

观察代码,我们使用变量vp来存储顶点处理类型。这是因为在稍后创建IDirect3DDevice9对象时要求指定其顶点处理的类型。

注意:标识符D3DCREATE_HARDWARE_VERTEXPROCESSING和D3DCREATE_SOFTWARE_VERTEXPROCESSING是预定义的值,它们分别代表硬件顶点处理和软件顶点处理。

技巧:若我们开发有一些新的,高级特性的程序,在使用前我们总是先检查硬件是否支持这些特性。

注意:如果一个应用程序在你的机子上不能运行,说明它用到的一些特性可能你的显卡并不支持,可以试试把设备类型换成REF。


--  作者:卷积内核
--  发布时间:11/27/2008 8:28:00 AM

--  
1.4.3 填充D3DPRESENT_PARAMETERS结构初始化过程的下一步是填充一个D3DPRESENT_PARAMETERS结构的实例。这个结构用于设定我们将要创建的IDirect3DDevice9对象的一些特性,它的定义如下:

typedef struct _D3DPRESENT_PARAMETERS_ {

       UINT BackBufferWidth;

       UINT 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;

下面介绍其比较重要的数据成员,至于更详细的信息,请查阅SDK:

BackBufferWidth——后备缓冲表面的宽度(以像素为单位)

BackBufferHeight——后备缓冲表面的高度(以像素为单位)

BackBufferFormat——后备缓冲表面的像素格式(如:32位像素格式为D3DFMT——A8R8G8B8)

BackBufferCount——后备缓冲表面的数量,通常设为“1”,即只有一个后备表面

MultiSampleType——全屏抗锯齿的类型,详情请看SDK

MultiSampleQuality——全屏抗锯齿的质量等级,详情看SDK

SwapEffect——指定表面在交换链中是如何被交换的,取D3DSWAPEFFECT枚举类型中的一个成员。其中D3DSWAPEFFECT_DISCARD是最有效的

hDeviceWindow——与设备相关的窗口句柄,你想在哪个窗口绘制就写那个窗口的句柄

Windowed——BOOL型,设为true则为窗口模式,false则为全屏模式

EnableAutoDepthStencil——设为true,D3D将自动创建深度/模版缓冲

AutoDepthStencilFormat——深度/模版缓冲的格式

Flags——一些附加特性,设为0或D3DPRESENTFLAG类型的一个成员。下列两个最常用的标志

全部的标志请查阅SDK:

D3DPRESENTFLAG_LOCKABLE_BACKBUFFER——设定后备表面能够被锁定,这会降低应用程序的性能

D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL——深度/模版缓冲在调用IDirect3DDevice9::present方法后将被删除,这有利于提升程序性能

FullScreen_RefreshRateInHz——刷新率,设定D3DPRESENT_RATE_DEFAULT使用默认刷新率

PresentationInterval——属于D3DPRESENT成员,又有两个常用标志,其余请查SDK:

         D3DPRESENT_INTERVAL_IMMEDIATE——立即交换

         D3DPRESENT_INTERVAL_DEFAULT——D3D选择交换速度,通常等于刷新率

填充示例如下:

D3DPRESENT_PARAMETERS d3dpp;

d3dpp.BackBufferWidth = 800;

d3dpp.BackBufferHeight = 600;

d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; //像素格式

d3dpp.BackBufferCount = 1;

d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;

d3dpp.MultiSampleQuality = 0;

d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

d3dpp.hDeviceWindow = hwnd;

d3dpp.Windowed = false; // fullscreen

d3dpp.EnableAutoDepthStencil = true;

d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; // depth format

d3dpp.Flags = 0;

d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;

d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

1.4.4 创建IDirect3DDevice9对象在填充完了D3DPRESENT_PARAMETERS结构后,我们就可以用下面的方法创建一个IDirect3DDevice9对象了:

HRESULT IDirect3D9::CreateDevice(

       UINT Adapter,

       D3DDEVTYPE DeviceType,

       HWND hFocusWindow,

       DWORD BehaviorFlags,

       D3DPRESENT_PARAMETERS *pPresentationParameters,

       IDirect3DDevice9** ppReturnedDeviceInterface

);

Adapter——指定对象要表示的物理显示设备

DeviceType——设备类型,前面说过

hFocusWindow——同我们在前面d3dpp.hDeviceWindow的相同

BehaviorFlags——设定为D3DCREATE_SOFTWARE_VERTEXPROCESSING或者D3DCREATE_HARDWARE_VERTEXPROCESSING

pPresentationParameters——指定一个已经初始化好的D3DPRESENT_PARAMETERS实例

ppReturnedDeviceInterface——返回创建的设备

例子:


IDirect3DDevice9* device = 0;

hr = d3d9->CreateDevice(
       D3DADAPTER_DEFAULT, // primary adapter
       D3DDEVTYPE_HAL, // device type
       hwnd, // window associated with device
       D3DCREATE_HARDWARE_VERTEXPROCESSING, // vertex processing type
       &d3dpp, // present parameters
       &device); // returned created device

if( FAILED(hr) )
{
       ::MessageBox(0, "CreateDevice() - FAILED", 0, 0);
       return 0;
}


--  作者:卷积内核
--  发布时间:11/27/2008 8:29:00 AM

--  
1.5初始化Direct3D实例
在本例程中,初始化了一个Direct3D应用程序并用黑色填充显示窗口(如图1.7)。

 

按此在新窗口浏览图片

                                          图1.7

 

所有的应用程序都包含了d3dUtility.h和d3dUtility.cpp这两个文件,它们所包含的函数实现了所有Direct3D应用程序都要去做的一些常见的功能。例如:创建一个窗口、初始化Direct3D、进入程序的消息循环等。

1.5.1d3dUtility.h/cpp
让我们先熟悉一下d3dUtility.h/cpp所提供的函数。d3dUtility.h如下:

    #include <d3dx9.h>
    
    template<typename T>
    void safe_release(T obj)
    {
        if(obj == NULL)
            return;
    
        obj->Release();
        obj = NULL;
    }
    
    template<typename T>
    void safe_delete(T obj)
    {
        if(obj == NULL)
            return;
    
        delete obj;
        obj = NULL;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////
    
    typedef bool (*DISPLAY_FUNC_PTR)(float timeDelta);
    
    bool init_d3d(HINSTANCE instance,            // application instance
                  int width, int height,        // backbuffer dimensions
                  bool is_window,                // true - windowed mode, false - full screen mode.
                  D3DDEVTYPE device_type,        // HAL or REF
                      IDirect3DDevice9** device);    // the create device
    
    int enter_msg_loop(DISPLAY_FUNC_PTR display);
    
    LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
init_d3d——初始化一个应用程序主窗口并进行Direct3D的初始化。如果成功,则输出IDirect3DDevice9接口指针。从它的参数我们可以发现,我们能够设置窗口的大小和以窗口模式运行还是全屏模式运行。要知道它实现的细节,请看示例代码。

    //-----------------------------------------------------------------------
    // Initialize windows and direct 3D.
    //-----------------------------------------------------------------------
    bool init_d3d(HINSTANCE instance,            // application instance
                  int width, int height,        // backbuffer dimensions
                  bool is_window,                // true - windowed mode, false - full screen mode.
                  D3DDEVTYPE device_type,        // HAL or REF
                      IDirect3DDevice9** device)    // the create device
    {
        const char* classname = "Direct3D9App";
    
        WNDCLASS wc;
        
        wc.style         = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc   = wnd_proc;
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = 0;
        wc.hInstance     = instance;
        wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        wc.hCursor         = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
        wc.lpszMenuName  = NULL;
        wc.lpszClassName = classname;
    
        if(! RegisterClass(&wc))
        {
            MessageBox(NULL, "RegisterClass() - failed.", NULL, MB_OK);
            return false;
        }
    
        HWND hwnd = CreateWindow(classname, "Direct3D9App", WS_EX_TOPMOST,
                                 0, 0, width, height, NULL, NULL, instance, NULL);
        
        if(hwnd == NULL)
        {
            MessageBox(NULL, "CreateWindow() - failed.", NULL, MB_OK);
            return false;
        }
    
        ShowWindow(hwnd, SW_SHOW);
        UpdateWindow(hwnd);
    
        // initialize D3D
    
        // step 1: Create the IDirect3D9 object.
    
        IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
    
        if(d3d9 == NULL)
        {
            MessageBox(NULL, "Direct3DCreate9() - failed.", NULL, MB_OK);
            return false;
        }
    
        // step 2: check for hardware vertex presentation.
        
        D3DCAPS9 caps;    
        d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, device_type, &caps);
        
        int vp = 0;
        if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
            vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
        else
            vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
    
        // step 3: fill out the D3DPRESENT_PARAMETERS structure.
    
        D3DPRESENT_PARAMETERS d3dpp;
    
        d3dpp.BackBufferWidth                = width;
        d3dpp.BackBufferHeight                = height;
        d3dpp.BackBufferFormat                = D3DFMT_A8R8G8B8;
        d3dpp.BackBufferCount                = 1;
        d3dpp.MultiSampleType                = D3DMULTISAMPLE_NONE;
        d3dpp.MultiSampleQuality            = 0;
        d3dpp.SwapEffect                    = D3DSWAPEFFECT_DISCARD;
        d3dpp.hDeviceWindow                    = hwnd;
        d3dpp.Windowed                        = is_window;
        d3dpp.EnableAutoDepthStencil        = true;
        d3dpp.AutoDepthStencilFormat        = D3DFMT_D24S8;
        d3dpp.Flags                            = 0;
        d3dpp.FullScreen_RefreshRateInHz    = D3DPRESENT_RATE_DEFAULT;
        d3dpp.PresentationInterval            = D3DPRESENT_INTERVAL_IMMEDIATE;
    
        // step 4: create the device.
    
        if(FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, device_type, hwnd, vp, &d3dpp, device)))
        {
            // try again using a 16-bit depth buffer
                d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
    
            if(FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, device_type, hwnd, vp, &d3dpp, device)))
            {
                d3d9->Release();    // done with d3d9 object
                MessageBox(NULL, "CreateDevice() - failed.", NULL, MB_OK);
                return false;
            }
        }
    
        d3d9->Release();        // done with d3d9 object
        return true;
    }
enter_msg_loop——这个函数封装了应用程序的消息循环。它需要输入一个显示函数的函数指针,显示函数为程序中绘制图形的代码块,这样做是为了使显示函数能够在空闲的时候被调用并显示场景,它的实现如下:

    //-----------------------------------------------------------------------
    // Enter windows message loop and render game frames if there is no message
    // comes from thread message queue.
    //-----------------------------------------------------------------------
    int enter_msg_loop(DISPLAY_FUNC_PTR display)
    {
        MSG msg;
        ZeroMemory(&msg, sizeof(MSG));
        
        // The timeGetTime function retrieves the system time, in milliseconds.
        // The system time is the time elapsed since Windows was started.    
        static float last_time = (float) timeGetTime();
    
        while(msg.message != WM_QUIT)
        {
            // The PeekMessage function dispatches incoming sent messages, checks the thread message queue for a
            // posted message, and retrieves the message (if any exist).
            //
            // If a message is available, the return value is nonzero.
            // If no messages are available, the return value is zero.
    
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else
            {
                float curr_time  = (float) timeGetTime();
                float time_delta = (curr_time - last_time) * 0.001f;
    
                display(time_delta);
    
                last_time = curr_time;
            }
        }
    
        return (int) msg.wParam;
    }
与“time”有关的代码用于计算每次调用显示函数的时间间隔,即是每帧的时间。

safe_release——这个模版函数能方便的释放COM接口并将它们的值设为NULL

safe_delete——这个模版函数能方便的删除一个对象并将指向其的指针设为NULL

wnd_proc——应用程序主窗口的回调函数


--  作者:卷积内核
--  发布时间:11/27/2008 8:31:00 AM

--  
1.5.2 实例框架通过实例框架,我们形成了一种通用的方法去构造示例程序。每一个例程都含有三个函数的实现,当然这不包括回调函数和WinMain主函数。这三个函数用特定的代码实现特定的功能。这三个函数是:

bool setup()——在这个函数里,我们将准备一切该程序需要用到的东西,包括资源的分配,检查设备能力,设置应用程序的状态

void clearup()——这个函数将释放Setup()中分配的资源,如分配的内存。

bool display(float time_delta)——这个函数包含所有与我们绘图和显示有关的代码。参数timeDelta为每一帧的间隔时间,用来控制每秒的帧数。

 

这个示例程序将创建并初始化一个Direct3D应用程序,并用黑色填充屏幕。注意,我们使用了通用函数简化了初始化过程。

    /*********************************************************************************
    PURPOISE:
        Demonstrates how to initialize Direct3D, how to use framework functions,
        and how to clear the screen to black.     
    *********************************************************************************/
    
    #include "D3DUtility.h"
    
    IDirect3DDevice9* g_device = NULL;
    
    bool setup()
    {
        // nothing to setup in this sample
    
        return true;
    }
    
    void cleanup()
    {
        // nothing to cleanup in this sample
    }
    
    bool display(float timeDelta)
    {
        // Only use Device methods if we have a valid device.
        if(g_device == NULL)
            return false;
    
        // Instruct the device to set each pixel on the back buffer black - D3DCLEAR_TARGET: 0x00000000 (black);
        // and to set each pixel on the depth buffer to a value of 1.0 - D3DCLEAR_ZBUFFER: 1.0f.
        g_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
    
        // swap the back and front buffers
        g_device->Present(NULL, NULL, NULL, NULL);
    
        return true;
    }
    
    LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch(msg)
        {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    
        case WM_KEYDOWN:
            if(wParam == VK_ESCAPE)
                DestroyWindow(hwnd);
            break;
        }
    
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    
    int WINAPI WinMain(HINSTANCE inst, HINSTANCE, PSTR cmd_line, int cmd_show)
    {
        if(! init_d3d(inst, 640, 480, true, D3DDEVTYPE_HAL, &g_device))
        {
            MessageBox(NULL, "init_d3d() - failed.", 0, MB_OK);
            return 0;
        }
    
        if(! setup())
        {
            MessageBox(NULL, "Steup() - failed.", 0, MB_OK);
            return 0;
        }
    
        enter_msg_loop(display);
    
        cleanup();
        g_device->Release();
    
        return 0;
    }
Display方法调用了IDirect3DDevice::Clear方法,分别用黑色和1.0填充后备表面和深度/模版缓冲。如果应用程序不停止的话,我们会一直执行这个操作。IDirect3DDevice::Clear声明如下:

HRESULT Clear( DWORD Count, CONST D3DRECT * pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil );

Count——pRects   组中的矩形的个数

pRects——将要清除的屏幕矩形的数组,这使我们可以清除屏幕的某一部分

Flags——指定在哪些表面上执行清除表面的操作

         D3DCLEAR_TARGET——目的表面,通常为后备表面

         D3DCLEAR_ZBUFFER——深度缓冲

         D3DCLEAR_STENCIL——模版缓冲

Color——使用什么颜色填充清除的表面

Z——设置深度缓冲的值

Stencil——设置模版缓冲的值

屏幕被填充后,要调用IDirecte3DDevice9::Present方法进行后备表面的交换。

 

Windows 回调函数为一组事件集,即,我们可按ESC键让程序退出。

 最后,WinMain按如下步骤运行:

1.        初始化主显示窗口和Direct3D

2.        调用setup进行程序的准备工作

3.        使用display函数作为参数进入消息循环

4.        清除应用程序最后释放IDirecte3DDevice9对象

 

注意:不要忘了在你的工程中加入d3d9.lib、d3dx9.lib、winmm.lib这三个库!


下载源码


--  作者:卷积内核
--  发布时间:11/27/2008 8:35:00 AM

--  
兄弟们,为方便大家查阅上面有置顶汇总贴,请参考:
http://www.ieee.org.cn/dispbbs.asp?boardID=61&ID=69838


[此贴子已经被admin于2009-3-16 15:10:42编辑过]

W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
140.625ms