以文本方式查看主题 - 中文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设备 1.1.2 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; 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 一些准备工作 1.3.1 表面
表面的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); If the method fails, the return value can be D3DERR_INVALIDCALL or D3DERR_WASSTILLDRAWING. Remarks 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(); Return Values
l GetDesc——这个方法将通过填充D3DSURFACE_DESC结构来返回表面的描述信息。
D3DSURFACE_DESC 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; HRESULT GetDesc( D3DSURFACE_DESC * pDesc); D3DERR_INVALIDCALL is returned if the argument is invalid.
最初锁定表面和改写每一像素看来稍微有点迷茫。下面的代码表示锁定表面并将每一像素染成红色: // Assume _surface is a pointer to an IDirect3DSurface9 interface. // Get the surface description. // Get a pointer to the surface pixel data. _surface->LockRect( // Iterate through each pixel in the surface and set it to red. for(int i = 0; i < surfaceDesc.Height; i++) imageData[index] = 0xffff0000; // red _surface->UnlockRect(); 程序中D3DLOCKED_RECT结构的定义如下: typedef struct _D3DLOCKED RECT { |
-- 作者:卷积内核 -- 发布时间: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) AGP2.0(AGP4X) AGP Pro AGP 3.0(AGP8X) AGP接口的模式传输方式 目前常用的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 交换链和页面切换 交换链以及页面切换技巧被用在使两帧动画之间过度更平滑。图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 深度缓冲 图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 顶点处理 使用硬件顶点处理总是首选,因为它比软件方式更快,而且不占用CPU资源,这意味CPU至少可以有更多的空闲时间进行别的计算。 注意:如果一块显卡支持硬件顶点处理的话,也就是说它也支持硬件几何转换和光源计算。 1.3.8 设备能力 下面将举例说明,假设我们想要检测显卡是否支持硬件顶点处理(换句话说,就是显卡是否支持硬件几何转换和光源计算)。通过查阅SDK中的D3DCAPS9结构,可以得知数据成员D3DCAPS9::DevCaps中的D3DDEVCAPS_HWTRANSFORMANDLIGHT位表示硬件是否支持硬件顶点处理即硬件几何变换和光源计算。程序如下: // If the bit is "on" then that implies the hardware device supports it.
注意: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( // Can we use hardware vertex processing? int vp = 0; if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
观察代码,我们使用变量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; 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——返回创建的设备 例子: hr = d3d9->CreateDevice( if( FAILED(hr) ) |
-- 作者:卷积内核 -- 发布时间: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 #include <d3dx9.h> //----------------------------------------------------------------------- //----------------------------------------------------------------------- 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应用程序,并用黑色填充屏幕。注意,我们使用了通用函数简化了初始化过程。 /********************************************************************************* 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 |