本文摘自百度空间-飘忽木头
http://hi.baidu.com/357802636/blog/item/4a669ff98b29ced2b48f319c.html
感谢原作者
使用Direct3D实现2D功能,很简洁的方式就是用Sprite的Draw功能,将Texture装载的图片画出来。刚完成了使用Direct3D代替DirectDraw,使C2的Windows模拟器能在保证效率的情况下很好的支持图片的alpha通道具体实现如下:
在Direct9的lib支持下,定义全局变量:
static LPDIRECT3D9 g_pD3D = NULL; // Used to create the D3DDevice
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; // Our rendering device
static ID3DXSprite* g_pd3dSprite = NULL;
初始化windows窗口:
HRESULT InitWindow(HWND &hWnd, WNDCLASSEX &wc)
{
// Register the window class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_CLASSDC;
wc.lpfnWndProc = MsgProc;
wc.cbClsExtra = 0L;
wc.cbWndExtra = 0L;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = CLASS_NAME;
wc.hIconSm = NULL;
RegisterClassEx( &wc );
// Create the application's window
hWnd = CreateWindow( CLASS_NAME
,WINDOW_NAME
,WS_OVERLAPPEDWINDOW
, 0, 0
, WIN_WIDTH, WIN_HEIGHT
,GetDesktopWindow()
, NULL
, wc.hInstance
, NULL );
if (!hWnd)
{
return S_FALSE;
}
return S_OK;
}
初始化D3D设备:
HRESULT InitD3D( HWND hWnd )
{
// Create the D3D object, which is needed to create the D3DDevice.
//这个参数告诉Direct3D使用正确的头文件,这个值会在任何时间头文件或其它改变需要重新建立工程时增加
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Get the current desktop display mode
D3DDISPLAYMODE d3ddm;
if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
return E_FAIL;
// Set up the structure used to create the D3DDevice. Most parameters are
// zeroed out. We set Windowed to TRUE, since we want to do D3D in a
// window, and then set the SwapEffect to "discard", which is the most
// efficient method of presenting the back buffer to the display. And
// we request a back buffer format that matches the current desktop display
// format.
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;//窗口化
d3dpp.BackBufferHeight = WIN_HEIGHT;
d3dpp.BackBufferWidth = WIN_WIDTH;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferCount = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//交换缓冲支持的效果类型,每次的表面翻页后,背景表面的数据都给丢弃
d3dpp.BackBufferFormat = d3ddm.Format;//后备缓冲的格式
// Create the Direct3D device. Here we are using the default adapter (most
// systems only have one, unless they have multiple graphics hardware cards
// installed) and requesting the HAL (which is saying we want the hardware
// device rather than a software one). Software vertex processing is
// specified since we know it will work on all cards. On cards that support
// hardware vertex processing, though, we would see a big performance gain
// by specifying hardware vertex processing.
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT/*主显卡的ID*/
,D3DDEVTYPE_HAL
, hWnd/*视窗的ID*/
,D3DCREATE_SOFTWARE_VERTEXPROCESSING/*3D顶点的处理方式*/
,&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}
// Device state would normally be set here
return S_OK;
}
初始化Sprite:
HRESULT InitD3DSprite()
{
return D3DXCreateSprite(g_pd3dDevice,&g_pd3dSprite);
}
加载图片到Texture对象中的方法:
HRESULT LoadTextureTest()
{
D3DXCreateTextureFromFileEx(g_pd3dDevice
,"./Data.Out/SPR_76EE68075757_SPR.png"
,D3DX_DEFAULT_NONPOW2//215//D3DX_DEFAULT 不被拉升为2次幂
,D3DX_DEFAULT_NONPOW2//174//D3DX_DEFAULT
,1,
0,
D3DFMT_UNKNOWN,
D3DPOOL_MANAGED,
D3DX_DEFAULT,
D3DX_DEFAULT,
0xffff00ff,
NULL,
NULL,
&g_pd3dTexture1);
return D3DXCreateTextureFromFileEx(g_pd3dDevice
,"./Debug/test001.png"
,D3DX_DEFAULT_NONPOW2
,D3DX_DEFAULT_NONPOW2
,1,
0,
D3DFMT_UNKNOWN,
D3DPOOL_MANAGED,
D3DX_DEFAULT,
D3DX_DEFAULT,
0xFF000000,
NULL,
NULL,
&g_pd3dTexture);
}
其中g_pd3dTexture和g_pd3dTexture1是两个IDirect3DTexture9指针对象,这里只是列举加载图片的方法。
通过Sprite画图:
void D3DDraw_SpriteTest()
{
if( NULL == g_pd3dDevice )
return;
if (NULL == g_pd3dSprite)
{
return;
}
g_pd3dDevice->Clear( 0/*多少个长方形需要清理*/
, NULL/*指针指向一个长方体数组*/
,D3DCLEAR_TARGET/*清除的目标*/
, D3DCOLOR_XRGB(0,255,0)
, 1.0f/*Z值,0.0是深度离盯着屏幕的观众最近,1.0是最远*/
, 0 );
// Begin the scene
g_pd3dDevice->BeginScene();
//Begin参数:
/*D3DXSPRITE_DONOTSAVESTATE 调用Begin()或End()不保存/恢复设备状态. (如pd3dDevice->SetRenderState中设置的部分状态)
D3DXSPRITE_DONOTMODIFY_RENDERSTATE 不是很清楚, 呵呵, 表面上看好像是不改变渲染状态...
D3DXSPRITE_OBJECTSPACE 不改变世界矩阵(WORLD)/投影矩阵(TRANSFORM)以及视点矩阵(VIEW), 使用设置在D3DDevice上的矩阵, 如果不指定这个标志, 3个矩阵自动改变为屏幕空间座标
D3DXSPRITE_BILLBOARD BillBoard, 很清楚吧, 所有的Sprite都全部自动旋转来对着观看着
D3DXSPRITE_ALPHABLEND 让Sprite支持AlphaBlend, 很重要, 几乎每次调用Begin都要指定此标志, 另外, D3DRS_ALPHATESTENABLE 状态必须设置为 TRUE, D3DBLEND_SRCALPHA / D3DBLEND_INVSRCALPHA 分别为源混和状态和目标很合状态
D3DXSPRITE_SORT_TEXTURE Sprite会按照渲染先后排序, 当渲染在同一个深度的Sprite推荐使用.
D3DXSPRITE_SORT_DEPTH_FRONTTOBACK 按照从前到后的渲染顺序对Sprite排序, 当在不同深度渲染有透明信息的Sprite时推荐使用.
D3DXSPRITE_SORT_DEPTH_BACKTOFRONT 按照从后到前的渲染顺序对Sprite排序, 当在不同深度渲染透明Sprite时推荐使用*/
if (FAILED(g_pd3dSprite->Begin(D3DXSPRITE_ALPHABLEND)))/*让Sprite支持AlphaBlend*/
{
return;
}
//SetupMatrices();
D3DXVECTOR3 tex_Center,tex_Postion;
tex_Center.x = 0;
tex_Center.y = 0;
tex_Center.z = 0;
tex_Postion.x = 0;
tex_Postion.y = 0;
tex_Postion.z = 0;
RECT rect = {0,0,WIN_WIDTH-5,WIN_HEIGHT-5};
//D3DXMATRIX mat;
//D3DXMatrixTransformation2D(&mat
// ,&D3DXVECTOR2(0,240)/*缩放中心*/
// ,0/*ScalingRotation*/
// ,&D3DXVECTOR2(0.9f,0.9f)/*缩放因子*/
// ,&D3DXVECTOR2(WIN_WIDTH/2, WIN_HEIGHT/2)/*旋转中心*/
// , 0/*旋转因子*/
// , &D3DXVECTOR2(0, 0));/*绘图的位置*/
//g_pd3dSprite->SetTransform(&mat);
g_pd3dSprite->Draw(g_pd3dTexture
,&rect
,&tex_Center /*中心默认为左上角*/
,&tex_Postion
,0xffFFffFF);
rect.top = 0;
rect.left = 0;
rect.right = 215;
rect.bottom = 174;
tex_Postion.x = 0;
tex_Postion.y = 240;
tex_Postion.z = 0;
g_pd3dSprite->Draw(g_pd3dTexture1
,&rect
,NULL /*中心默认为左上角*/
,&tex_Postion
,0xffffffff);
g_pd3dSprite->End();
g_pd3dDevice->EndScene();
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
//ValidateRect( g_hwnd, NULL );
}
其他的一些方法实现如下:
清空Texture中的某个区域
void ClearImage(void *pSurface,void *rect)
{
RECT *dr = (RECT*)rect;
LPDIRECT3DSURFACE9 pDestSurface = NULL;
((IDirect3DTexture9*)pSurface)->GetSurfaceLevel(0, &pDestSurface); // 纹理对应表面
D3DSURFACE_DESC DestsurfaceDesc;
pDestSurface->GetDesc(&DestsurfaceDesc);
D3DLOCKED_RECT DestlockedRect;
pDestSurface->LockRect(&DestlockedRect, 0, 0); // entire surface
DWORD* DestimageData = (DWORD*)DestlockedRect.pBits;
int ndest;
for (int i=dr->top;i<dr->bottom;++i)
{
for (int j=dr->left;j<dr->right;++j)
{
ndest = i*DestlockedRect.Pitch/4+j;
DestimageData[ndest]= 0xff000000;
}
}
pDestSurface->UnlockRect();
}
//************************************
// Method: DrawImage
// FullName: DrawImage
// Access: public
// Returns: void
// Qualifier: Texture相互拷贝
// Parameter: void * pDest
// Parameter: void * pDestRect
// Parameter: void * pSource
// Parameter: void * pSourceRect
// Parameter: bool bBlend
//************************************
void DrawImage(void *pDest,void *pDestRect,void *pSource,void *pSourceRect,bool bBlend)
{
RECT *dr = (RECT*)pDestRect;
RECT *sr = (RECT*)pSourceRect;
LPDIRECT3DSURFACE9 pDestSurface = NULL;
LPDIRECT3DSURFACE9 pSourceSurface = NULL;
((IDirect3DTexture9*)pDest)->GetSurfaceLevel(0, &pDestSurface); // 纹理对应表面
D3DSURFACE_DESC DestsurfaceDesc;
pDestSurface->GetDesc(&DestsurfaceDesc);
D3DLOCKED_RECT DestlockedRect;
pDestSurface->LockRect(&DestlockedRect, 0, 0); // entire surface
((IDirect3DTexture9*)pSource)->GetSurfaceLevel(0, &pSourceSurface); // 纹理对应表面
D3DSURFACE_DESC SourcesurfaceDesc;
pSourceSurface->GetDesc(&SourcesurfaceDesc);
D3DLOCKED_RECT SourcelockedRect;
pSourceSurface->LockRect(&SourcelockedRect, 0, 0); // entire surface
DWORD* DestimageData = (DWORD*)DestlockedRect.pBits;
DWORD* SourceimageData = (DWORD*)SourcelockedRect.pBits;
int sH = sr->top-1;
int sW = sr->left-1;
int ndest,nsource;
for (int i=dr->top;i<dr->bottom;++i)
{
if(++sH>=sr->bottom)
{
sH = sr->top;
}
for (int j=dr->left;j<dr->right;++j)
{
if (++sW>=sr->right)
{
sW = sr->left;
}
ndest = i*DestlockedRect.Pitch/4+j;
nsource = sH*SourcelockedRect.Pitch/4+sW;
DestimageData[ndest+0]= SourceimageData[nsource+0];
}
}
pDestSurface->UnlockRect();
pSourceSurface->UnlockRect();
}
创建空的Texture:
void* CreateEmemySuface(int ImageWidth,int ImageHeight)
{
//创建Direct3DTexture
IDirect3DTexture9* pd3dTexture = NULL;
if(S_OK != D3DXCreateTexture(g_pd3dDevice
,ImageWidth
,ImageHeight
,1
,0
,D3DFMT_A8B8G8R8
,D3DPOOL_MANAGED
,&pd3dTexture))
{
return NULL;
}
return (void *)pd3dTexture;
}
//************************************
// Method: BeginDrawSprite
// FullName: BeginDrawSprite
// Access: public
// Returns: void
// Qualifier: 开始画动画
// Parameter: void
//************************************
void BeginDrawSprite(void)
{
if( NULL == g_pd3dDevice )
return;
if (NULL == g_pd3dSprite)
{
return;
}
if (g_bDrawSprBegin)
{
return;
}
#if USE_DIRTYRECT == 0
//如果不用脏矩形,重绘之前需要清理所有的东西
g_pd3dDevice->Clear( 0/*多少个长方形需要清理*/
, NULL/*指针指向一个长方体数组*/
,D3DCLEAR_TARGET/*清除的目标*/
, D3DCOLOR_XRGB(0,0,0)
, 1.0f/*Z值,0.0是深度离盯着屏幕的观众最近,1.0是最远*/
, 0 );
#endif
g_pd3dDevice->BeginScene();
/*D3DXSPRITE_DONOTSAVESTATE 调用Begin()或End()不保存/恢复设备状态. (如pd3dDevice->SetRenderState中设置的部分状态)
D3DXSPRITE_DONOTMODIFY_RENDERSTATE 不是很清楚, 呵呵, 表面上看好像是不改变渲染状态...
D3DXSPRITE_OBJECTSPACE 不改变世界矩阵(WORLD)/投影矩阵(TRANSFORM)以及视点矩阵(VIEW), 使用设置在D3DDevice上的矩阵, 如果不指定这个标志, 3个矩阵自动改变为屏幕空间座标
D3DXSPRITE_BILLBOARD BillBoard, 很清楚吧, 所有的Sprite都全部自动旋转来对着观看着
D3DXSPRITE_ALPHABLEND 让Sprite支持AlphaBlend, 很重要, 几乎每次调用Begin都要指定此标志, 另外, D3DRS_ALPHATESTENABLE 状态必须设置为 TRUE, D3DBLEND_SRCALPHA / D3DBLEND_INVSRCALPHA 分别为源混和状态和目标很合状态
D3DXSPRITE_SORT_TEXTURE Sprite会按照渲染先后排序, 当渲染在同一个深度的Sprite推荐使用.
D3DXSPRITE_SORT_DEPTH_FRONTTOBACK 按照从前到后的渲染顺序对Sprite排序, 当在不同深度渲染有透明信息的Sprite时推荐使用.
D3DXSPRITE_SORT_DEPTH_BACKTOFRONT 按照从后到前的渲染顺序对Sprite排序, 当在不同深度渲染透明Sprite时推荐使用*/
if (FAILED(g_pd3dSprite->Begin(D3DXSPRITE_ALPHABLEND)))/*让Sprite支持AlphaBlend*/
{
return;
}
g_bDrawSprBegin = true;//标记为开启了drawSprite
}
void EndDrawSprite(void)
{
if( NULL == g_pd3dDevice )
return;
if (NULL == g_pd3dSprite)
{
return;
}
if (!g_bDrawSprBegin)
{//如果drawSprite没有开启,返回,保证不被重复开启或重复关闭
g_bReady=1;
return;
}
g_bDrawSprBegin = false;
g_pd3dSprite->End();
g_pd3dDevice->EndScene();
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
ShowGameFps();
g_bReady=1;
}