这一部分内容是关于
Direct3D设备用途及使用的一些概述,包括以下内容:在
DirectX 5.0中的IDirect3DDevice2接口介绍之前,Direct3D设备是DirectDrawSurface对象的接口。IDirect3DDevice2接口实现了一个设备对象模型(device-object model),在这个模型中,Direct3Ddevice对象与DirectDraw表面是完全分离的。IDirect3DDevice3接口使用并扩展了这一设备对象模型。因为它们独立于DirectDraw表面并且有独立的生存期,Direct3D 设备对象在不同的时间可以使用不同的DirectDraw表面来作为渲染目标。有关渲染目标的详细内容见IDirect3DDevice3::SetRenderTarget。 2. Direct3D设备类型这一部分介绍Direct3D设备和它的每一种类型。
注:D3d.h头文件为参考光栅(reference rasterizer)定义了一个第五设备标识符(IID_IDirect3DrefDevice)。这个参考光栅不是为正式程序设计的,它只被用于参考或特征演示时使用。它不能用通常的方法来列举,你必须在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Direct3D\Drivers注册键(registry key)中设置一个名为EnumReference的值,这样才能被列举。 2.1 关于设备类型 Direct3D支持四种类型的设备。程序创建的Direct3D设备必须与所使用的硬件相匹配。Direct3D提供两种渲染能力,一种是通过3-D硬件,另一种是对3-D硬件功能的软件方针。因此,Direct3D提供的设备对于硬件访问和软件仿真都予以支持。硬件加速设备比软件仿真设备有更好的性能。
除了参考光栅(
reference rasterizer)之外,软件设备并不支持硬件设备的所具有的特性。例如,软件设备就不支持同时将一个纹理分派到两个以上的纹理stage(texture 平台)。程序应该对设备能力经常进行查询以确定支持哪些特性。 2.2 HAL设备如果程序运行的计算机的显示适配器支持Direct3D,那么程序就可以用它来进行3-D操作。Direct3D兼容加速卡在硬件中实现了所有或部分的变换、灯光和光栅模块。
应用程序不能直接访问3-D加速卡。他们要调用Direct3D的函数和方法。Direct3D通过HAL来访问硬件。如果程序运行的计算机支持HAL,那么使用HAL设备就能获得最好的性能。
为了创建一个HAL设备,程序要调用IDirect3D3::CreateDevice,并且将IID_IDirect3DHALDevice作为它的低一个参数。详细内容见“创建一个Direct3D设备”。
注:与软件仿真设备(MMX, RGB,参考光栅和传统的ramp设备)不同的是,硬件设备不能在8-bit渲染目标表面上进行渲染。
MMX是一套特殊的指令集,一些微处理器提供对它的支持,并由此而获得了更好的多媒体和三维处理性能。如果安装了MMX,那么Direct3D会使用MMX来提高渲染速度。
MMX设备不象HAL设备那样,它并不是硬件加速设备。变换、灯光和光栅模块都是由软件来执行的。但是,MMX设备提供了比其它的软件仿真设备更好的性能。
如果程序使用的是Direct3D2接口,那么它就必须明确的创建一个MMX设备。但是,从IDirect3D3接口开始,MMX和RGB设备将执行同样的特性设置。如果程序需要创建一个RGB设备并且微处理器支持MMX技术的话,那么Direct3D将自动创建一个MMX设备。
引用IDirect3D3::CreateDevice方法,将IID_IDirect3DMMXDevice作为第一个参数,这样就可以创建一个MMX设备。有关创建Direct3D设备的详细内容见“创建一个Direct3D设备”。如果程序要求创建一个MMX设备,然而计算机并不支持MMX技术,那么CreateDevice方法就会失败。
如果计算机不提供对三维操作的硬件加速,那么程序可以使用软件来对三维硬件进行仿真。如果你的计算机不支持MMX技术,但却有足够的处理能力,那么程序就可以使用RGB设备。RGB设备通过软件来仿真3-D硬件的色彩处理功能。因为RGB设备要通过软件来进行仿真,所以它的速度就要比HAL设备慢,也要比MMX设备慢。详细内容见“仿真模式”。
如果你所使用的机器支持MMX技术,那么当程序创建一个RGB设备时,IDirect3D3接口会自动创建一个MMX设备。
程序使用IDirect3D3::CreateDevice函数来创建一个RGB设备,同时要将IID_IDirect3DRGBDevice作为它的第一个参数。
注:ramp软件仿真驱动器在DirectX中已经被废除了,DirectX 6.0以及后续版本对它均不再提供支持。使用IDirect3D3不能创建一个Direct3D ramp设备,也不能使用IDirect3DDevice3接口来查询一个已经存在的ramp设备。简单来说,ramp设备不支持任何的多纹理融合操作。要想实现对这些特性的方针,就必须使用MMX或RGB软件仿真设备。一个ramp设备是一个软件仿真设备,它可以提供单色光。如果你的计算机没有足够的处理能力来使用其它的Direct3D设备,那么你可以选择使用ramp设备。详细内容见“仿真模式”。
Ramp设备主要是为了为传统的DirectX程序提供支持。一般而言,没有足够的处理能力来支持RGB设备的计算机也是不适于3-D程序的。
程序通过调用IDirect3D2::CreateDevice方法来创建ramp设备,同时要将IID_IDirect3DrampDevice作为第一个参数。
3. 设备接口使用设备接口,主要是用来操作一个
Direct3Ddevice对象的渲染状态、灯光状态以及执行渲染操作。尽管设备支持三个设备接口(IDirect3DDevice, IDirect3DDevice2, 和IDirect3DDevice3),但是你的程序中并不需要使用多个设备接口。使用那种接口,要有所使用的渲染方法来决定,即DrawPrimitive方法或执行缓冲方法。下面我们来讨论一下这三种接口以及它们所代表的渲染方法: 3.1 执行缓冲 IDirect3Ddevice支持使用执行缓冲来进行渲染,它是Direct3D程序最初使用的方法。这个接口在后来的DirecX版本中是兼容的,但是DrawPrimitive渲染结构更容易使用。我们推荐使用IDirect3DDevice3接口来编制新的应用程序。所有
Direct3D设备类型都支持IDirect3Ddevice接口。 3.2 DrawPrimitive渲染最新的两个设备接口是IDirect3DDevice3和IDirect3DDevice2,它们都支持DrawPrimitive 渲染方法和执行缓冲。DrawPrimitive方法大大简化了准备和渲染顶点的过程,并被认为是首选的渲染方法。
如果你的程序使用DrawPrimitive方法,那么最好使用最新版本的设备接口。符合COM 标准的向后兼容性的Direct3D支持所有的接口版本,但我们还是推荐使用最新的版本,这样就可以利用到一些新的特性,并且能提高程序的性能。
DirectX 5.0中创建的IDirect3DDevice2接口首次提出了DrawPrimitive渲染结构。基于DrawPrimitive的渲染方法比执行缓冲更容易使用,并且提供了用于Direct3D编程的更直接的形式。详细内容见“渲染”部分。所有的Direct3D设备类型都支持IDirect3DDevice2接口。
象IDirect3DDevice2接口一样,IDirect3DDevice3接口也支持DrawPrimitive方法。相应于IDirect3D3接口,IDirect3DDevice2接口提供了增强的特性集,它包括多纹理融合、顶点缓冲以及增强的3-D几何渲染管道。IDirect3DDevice3接口也提供了IDirect3DDevice2接口中的同样的方法。同时它也为IDirect3DDevice3::SetRenderState方法增加了一些新的渲染状态以支持多纹理融合。
所有的Direct3D设备类型都支持IDirect3DDevice3接口。
4. 使用设备这一部分我们将讨论如何在立即模式中使用
Direct3D设备。这一部分我们来讨论列举Direct3D设备时的两个主要的任务。
程序可以对所使用的硬件进行查询,这样就可以确定它支持哪种Direct3D设备。完成这一任务最重要的API函数是IDirect3D3::EnumDevices,它用来枚举所有硬件可能支持的Direct3D设备。它使用D3DenumDevicesCallback函数来选择要使用的设备。D3DenumDevicesCallback函数由你在程序中来提供。注意:因为这个回调函数是由程序提供的,因此它的名字可以任意来取。
下面的代码展示了枚举和选择Direct3D设备的过程。在这个例子中,设备枚举回调函数被命名为EnumDeviceCallback。一个指向EnumDeviceCallback的指针被传递给IDirect3D3::EnumDevices方法,它为每个正在被枚举的设备调用EnumDeviceCallback函数。
// In this code fragment, the variable lpd3d contains a valid // pointer to the IDirect3D3 interface that the application obtained // prior to executing this code. BOOL fDeviceFound = FALSE; hRes = lpd3d->EnumDevices(EnumDeviceCallback, &fDeviceFound); if (FAILED(hRes)) { // Code to handle the error goes here. } if (!fDeviceFound) { // Code to handle the error goes here. } |
4.1.2 选择一个被枚举的设备 D3DenumDevicesCallback函数被每一个安装在系统中的Direct3D设备所引用。当它被调用时,它的第一个参数将作为正在被枚举的设备的全局统一标识符(GUID)。GUID的值将是IID_IDirect3DHALDevice,IID_IDirect3DMMXDevice,IID_IDirect3DRGBDevice或IID_IDirect3DrampDevice。D3DenumDevicesCallback函数最适合你的程序的那个设备。 D3DenumDevicesCallback函数的第二和第三个参数为字符串,它包含了设备的名称和用户友好的描述。
第四个参数指向一个
D3DDEVICEDESC结构体,它包含了设备的硬件性能的有关信息。即使被列举的设备是一个HAL设备,具体的硬件也不一定支持所有的Direct3D API所提供的性能。 D3DenumDevicesCallback的第五个参数包含了一个指向D3DDEVICEDESC结构体的指针,它用来描述你所使用的机器的软件仿真性能。这一信息与你要使用的软件仿真设备(MMX, RGB,或RAMP设备)有关。最后一个参数是一个程序定义的值。你的程序将这个参数传递给
IDirect3D3::EnumDevices方法。它于此将这个值传递给D3DenumDevicesCallback函数。下面的代码描述了如何创建一个
D3DenumDevicesCallback函数。在这个例子中,程序提供的回调函数名为EnumDeviceCallback。EnumDeviceCallback函数使用下面的算法来选择一个适合的Direct3D设备:// This function is written with the assumption that the following // global variables are declared in the program. // DWORD dwDeviceBitDepth = 0; // GUID guidDevice; // char szDeviceName[MAX_DEVICE_NAME]; // char szDeviceDesc[MAX_DEVICE_DESC]; // D3DDEVICEDESC d3dHWDeviceDesc; // D3DDEVICEDESC d3dSWDeviceDesc; static HRESULT WINAPI EnumDeviceCallback(LPGUID lpGUID, LPSTR lpszDeviceDesc, LPSTR lpszDeviceName, LPD3DDEVICEDESC lpd3dHWDeviceDesc, LPD3DDEVICEDESC lpd3dSWDeviceDesc, LPVOID lpUserArg) { BOOL fIsHardware; LPD3DDEVICEDESC lpd3dDeviceDesc; // If there is no hardware support the color model is zero. fIsHardware = (lpd3dHWDeviceDesc->dcmColorModel != 0); lpd3dDeviceDesc = (fIsHardware ? lpd3dHWDeviceDesc : lpd3dSWDeviceDesc); // Does the device render at the depth we want? if ((lpd3dDeviceDesc->dwDeviceRenderBitDepth & dwDeviceBitDepth) == 0) { // If not, skip this device. return D3DENUMRET_OK; } // The device must support Gouraud-shaded triangles. if (D3DCOLOR_MONO == lpd3dDeviceDesc->dcmColorModel) { if (!(lpd3dDeviceDesc->dpcTriCaps.dwShadeCaps&D3DPSHADECAPS_COLORGOURAUDMONO)) { // No Gouraud shading. Skip this device. return D3DENUMRET_OK; } } else { if (!(lpd3dDeviceDesc->dpcTriCaps.dwShadeCaps & D3DPSHADECAPS_COLORGOURAUDRGB)) { // No Gouraud shading. Skip this device. return D3DENUMRET_OK; } } // If a software device was found on a previous invocation // of this callback function. if (!fIsHardware && *lpUserArg && (D3DCOLOR_RGB == lpd3dDeviceDesc->dcmColorModel)) { // If this is software RGB and we already have found // a software monochromatic renderer, we are not // interested. Skip this device. return D3DENUMRET_OK; } // // This is a device we are interested in. Save the details. // *lpUserArg = TRUE; CopyMemory(&guidDevice, lpGUID, sizeof(GUID)); strcpy(szDeviceDesc, lpszDeviceDesc); strcpy(szDeviceName, lpszDeviceName); CopyMemory(&d3dHWDeviceDesc, lpd3dHWDeviceDesc, sizeof(D3DDEVICEDESC)); CopyMemory(&d3dSWDeviceDesc, lpd3dSWDeviceDesc, sizeof(D3DDEVICEDESC)); // If this is a hardware device, we have found // what we are looking for. if (fIsHardware) return D3DENUMRET_CANCEL; // Otherwise, keep looking. return D3DENUMRET_OK; } |
4.2 创建一个Direct3D设备
创建一个设备时,必须要选择Direct3D程序使用执行缓冲还是DrawPrimitive方法。这一选择将决定在创建一个设备时程序需要获得哪种类型的接口指针。如果选择使用执行缓冲,那么程序就必须得到一个指向IDirect3Ddevice接口的指针。如果选择DrawPrimitive方法,程序必须得到一个指向IDirect3DDevice3接口的指针。
下面的讨论将向我们展示如何来创建一个设备:
注:一些通用的硬件设备要求渲染目标与深度缓冲表面使用相同的颜色深度。在这些硬件上,如果程序在渲染目标表面使用16-bit色,那么相应的深度缓存也必须使用16-bit色。如果使用32-bit的渲染目标表面,那么深度缓存也必须是32-bits,如果需要的话,也可以使用8-bits的模板缓冲。要使用执行缓冲方法,程序必须先在常规方式下初始化DirectDraw,并得到一个指向IDirect3D3接口的指针。详细内容见“得到(retrive)一个IDirect3D3接口”。程序还应创建一个包含DDSCAPS_3DDEVICE能力的表面。关于创建表面的内容见“创建表面”部分。然后,对这个表面调用IUnknown::QueryInterface方法,得到一个指向IDirect3Ddevice接口的指针。使用IDirect3DDevice::CreateExecuteBuffer来创建一个执行缓冲,并得到一个指向IDirect3DexecuteBuffer接口的指针。下面左图描述了这一过程:
4.2.2 创建使用DrawPrimitive方法的设备
使用DrawPrimitive方法,程序也必须先在常规方式下初始化DirectDraw对象,并得到一个指向IDirect3D3接口。然后,调用IDirect3D3::CreateDevice方法创建一个Direct3D设备。这一方法传递给程序一个指向IDirect3DDevice3接口的指针。
上面右图描述了这一过程:
代码如下:
LPDIRECTDRAW lpDD; // DirectDraw Interface LPDIRECT3D3 lpD3D; // Direct3D3 Interface LPDIRECTDRAWSURFACE4 lpddsRender; // Rendering surface LPDIRECT3DDEVICE3 lpd3dDevice; // D3D Device // Create DirectDraw interface. // Use the current display driver. hResult = DirectDrawCreate (NULL, &lpDD,
NULL); } // Get an IDirect3D3 interface hResult = lpDD->QueryInterface (IID_IDirect3D3, (void **)&lpD3D); if (FAILED (hResult)) { // Code to handle the error goes here. } // // Code for the following tasks is omitted for clarity. // // Applications will need to set the cooperative level at this point. // Full-screen applications will probably need to set the display mode. // The primary surface should be created here. // The rendering surface must be created at this point. It is // assumed in this code fragment that, once created, the rendering // surface is pointed to by the variable lpddsRender. // If a z-buffer is being used, it should be created here. // Direct3D device enumeration can be done at this point. hResult = lpD3D->CreateDevice (IID_IDirect3DHALDevice, lpddsRender, &lpd3dDevice, NULL); |
上面的例子中引用了IDirect3D3::CreateDevice方法来创建一个Direct3D设备。在这个例子中,如果调用成功的话,就创建了一个Direct3D HAL设备。
要注意的时,程序中创建的DirectDraw渲染表面不允许作为一个Direct3D的渲染目标来创建。要这样做,就必须向IDirectDraw4::CreateSurface方法传递一个DDSURFACEDESC2结构体。DDSURFACEDESC2结构体由一个名为ddsCaps的成员,它是一个DDSCAPS类型的结构体。DDSCAPS结构体又包含了一个名为dwCaps的成员,在使用IDirectDraw4::CreateSurface方法时,它必须被设置为DDSCAPS_3DDEVICE。
当使用硬件加速渲染设备时,我们所使用的渲染目标表面必须在显存中来创建(使用DDSCAPS_VIDEOMEMORY标志),否则,在系统内存中来创建(使用DDSCAPS_SYSTEMMEMORY标志)。
使用IDirect3DDevice3::SetTransform 方法来应用变换。例如,你可以使用如下的代码来设置视变换:
HRESULT hr D3DMATRIX view; // Fill in the view matrix. hr = lpDev->SetTransform(D3DTRANSFORMSTATE_VIEW, &view); if(FAILED(hr)) return hr; |
调用IDirect3DDevice3::SetTransform方法时,它的第一个参数有三种可能的设置:D3DTRANSFORMSTATE_WORLD, D3DTRANSFORMSTATE_VIEW, 和D3DTRANSFORMSTATE_PROJECTION。这三种变换状态在D3DTRANSFORMSTATETYPE枚举类型中进行定义。