因为二维的游戏已经逐渐减少并且显卡对二维的支持也不再增加新的功能,所以从 DirectX7开始,DirectDraw 被逐渐淡化,代之的是 Direct3D ,但是在3D 中有时也是需要绘制二维的,且不说材质贴图的支持,就是一般的游戏特效(例如在近景是三维的,但是当镜头拉伸到较远的距离时,直接显示二维的地形图)也是需要二维处理的,当然在 Direct3D 中二维的处理方式比较多,最常用的是创建遮盖表面,然后将二维的图作为材质贴在这个表面上,该表面的法线总是指向摄像机并且距离保持恒定,但是这个过程却比较复杂,需要创建场景、摄像机、面片等,本文却介绍一种在 Direct3D 中很简单的二维绘制方法,主要用来进行简单的二维图形处理,首先是声明相关的头文件并连接对应的库:
#include
<
d3d9.h
>
#pragma comment( lib, "dxerr.lib" )
#pragma comment( lib, "dxguid.lib" )
#pragma comment( lib, "d3d9.lib" )
CComPtr < IDirect3D9 > spDirect3D;
CComPtr < IDirect3DDevice9 > spDirectDevice;
#pragma comment( lib, "dxerr.lib" )
#pragma comment( lib, "dxguid.lib" )
#pragma comment( lib, "d3d9.lib" )
CComPtr < IDirect3D9 > spDirect3D;
CComPtr < IDirect3DDevice9 > spDirectDevice;
然后创建 Direct3D 和 Direct3DDevice,参考如下的代码:
if
(
!
spDirect3D )
{
spDirect3D = Direct3DCreate9( D3D_SDK_VERSION );
CHK_EXP_RET( ! spDirect3D , ERROR_SUCCESS );
D3DPRESENT_PARAMETERS xD3DPP;
ZeroMemory( & xD3DPP, sizeof (xD3DPP) );
xD3DPP.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER ;
xD3DPP.SwapEffect = D3DSWAPEFFECT_COPY;
xD3DPP.Windowed = TRUE;
DWORD dwFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE ;
HRESULT hr = spDirect3D -> CreateDevice( D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , m_hWnd , dwFlags , & xD3DPP , & spDirectDevice );
CHK_EXP_RET( FAILED( hr ) , ERROR_SUCCESS );
}
{
spDirect3D = Direct3DCreate9( D3D_SDK_VERSION );
CHK_EXP_RET( ! spDirect3D , ERROR_SUCCESS );
D3DPRESENT_PARAMETERS xD3DPP;
ZeroMemory( & xD3DPP, sizeof (xD3DPP) );
xD3DPP.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER ;
xD3DPP.SwapEffect = D3DSWAPEFFECT_COPY;
xD3DPP.Windowed = TRUE;
DWORD dwFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE ;
HRESULT hr = spDirect3D -> CreateDevice( D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , m_hWnd , dwFlags , & xD3DPP , & spDirectDevice );
CHK_EXP_RET( FAILED( hr ) , ERROR_SUCCESS );
}
上面的代码中使用了一个简单的宏处理 CHK_EXP_RET ,其定义如下:
#define
CHK_EXP_RET( exp , ret ) if( exp ){ return (ret) ; }
创建了设备之后,在绘图时就可以使用如下的代码来进行处理了:
CComPtr
<
IDirect3DSurface9
>
spSurface;
HRESULT hr = spDirectDevice -> GetBackBuffer( 0 , 0 , D3DBACKBUFFER_TYPE_MONO , & spSurface );
CHK_EXP_RET( FAILED( hr ) , ERROR_SUCCESS );
HDC hDC;
hr = spSurface -> GetDC( & hDC );
DrawGraphics( hDC , rcView );
spSurface -> ReleaseDC( hDC );
hr = spDirectDevice -> Present( NULL , NULL , NULL , NULL );
HRESULT hr = spDirectDevice -> GetBackBuffer( 0 , 0 , D3DBACKBUFFER_TYPE_MONO , & spSurface );
CHK_EXP_RET( FAILED( hr ) , ERROR_SUCCESS );
HDC hDC;
hr = spSurface -> GetDC( & hDC );
DrawGraphics( hDC , rcView );
spSurface -> ReleaseDC( hDC );
hr = spDirectDevice -> Present( NULL , NULL , NULL , NULL );
请注意如果 GetBackBuffer 函数返回错误,那么请检查相关的参数是否指定正确,例如 D3DPRESENT_PARAMETERS 的成员 Flags 是否包含 D3DPRESENTFLAG_LOCKABLE_BACKBUFFER 标志;
具体的二维图形绘制可以在 DrawGraphics 中随心所遇的绘制,因为有 HDC ,也可以使用 GDI+ 进行处理;当然还是尽可能的使用 DirectX 中的接口以保证效率;另外如果需要直接写屏可以使用 spSurface->LockRect 方法,但是该方法和 GetDC 是互斥使用的,因为这两个方法会都锁定该表面的存储区;
一般情况下都希望有一个离线的表面来进行绘图,而前面代码中的 spSurface 仅仅是用来进行显示,那么可以使用 IDirect3DDevice::CreateOffscreenPlainSurface 函数来创建一个离线的表面,然后在该表面上进行绘图,然后使用 IDirect3DDevice::UpdateSurface 将该表面更新到显示表面上,这样就可以根据需要创建较多的离线表面,每个离线表面单独绘制,然后合成一个最终的图形;
最后,需要注意的是,如果是窗口模式的应用,那么在 WM_SIZE 消息中需要对 IDirect3DDevice 设备进行 Reset ,否则绘图表面的大小将永远是创建时的大小,这将导致窗口放缩时,绘制的原始图形大小不变,但是在屏幕上根据窗口的大小进行放缩,其实该函数调用很简单,如下所示:
具体的二维图形绘制可以在 DrawGraphics 中随心所遇的绘制,因为有 HDC ,也可以使用 GDI+ 进行处理;当然还是尽可能的使用 DirectX 中的接口以保证效率;另外如果需要直接写屏可以使用 spSurface->LockRect 方法,但是该方法和 GetDC 是互斥使用的,因为这两个方法会都锁定该表面的存储区;
一般情况下都希望有一个离线的表面来进行绘图,而前面代码中的 spSurface 仅仅是用来进行显示,那么可以使用 IDirect3DDevice::CreateOffscreenPlainSurface 函数来创建一个离线的表面,然后在该表面上进行绘图,然后使用 IDirect3DDevice::UpdateSurface 将该表面更新到显示表面上,这样就可以根据需要创建较多的离线表面,每个离线表面单独绘制,然后合成一个最终的图形;
最后,需要注意的是,如果是窗口模式的应用,那么在 WM_SIZE 消息中需要对 IDirect3DDevice 设备进行 Reset ,否则绘图表面的大小将永远是创建时的大小,这将导致窗口放缩时,绘制的原始图形大小不变,但是在屏幕上根据窗口的大小进行放缩,其实该函数调用很简单,如下所示:
CHK_EXP_RET(
!
spDirectDevice ,
0
);
D3DPRESENT_PARAMETERS xD3DPP;
ZeroMemory( & xD3DPP, sizeof (xD3DPP) );
xD3DPP.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER ;
xD3DPP.SwapEffect = D3DSWAPEFFECT_COPY;
xD3DPP.Windowed = TRUE;
spDirectDevice -> Reset( & xD3DPP);
return 0 ;
D3DPRESENT_PARAMETERS xD3DPP;
ZeroMemory( & xD3DPP, sizeof (xD3DPP) );
xD3DPP.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER ;
xD3DPP.SwapEffect = D3DSWAPEFFECT_COPY;
xD3DPP.Windowed = TRUE;
spDirectDevice -> Reset( & xD3DPP);
return 0 ;