【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 19540|回复: 27

[原创]通过ID3DXSprite来实现DirectX 9.0C绘制2D动画

[复制链接]
发表于 2005-8-11 01:51:08 | 显示全部楼层 |阅读模式

经常上论坛的朋友可能都发现了,最近我一直在不停地问有关2D游戏制做的技术。继上一篇DirectX9中的二维图片的加载以后,我再发一篇关于通过ID3DXSprite来实现DirectX 9.0C绘制2D动画的帖子,希望对大家有所帮助。

在DirectX 8以前,2D都是DirectDraw来完成的,是Direct中非常重要的一个部分,但是到了DirectX 8以后,DirectDraw被合并到DirectX Graphics 当中了,所以在那以后就很少有人谈到如何用DirectX Graphics 实现2D效果。大多数需要2D的人都不辞辛苦用DirectDraw来实现了,毕竟用Direct 7的东西,后面的都是支持的。但是正如一个老外说的,喜欢用DirectDraw的人用用也无所谓,从Direct 8 以后学习的人再去学DirectDraw就不合适了,那么怎么实现自己需要的2D效果呢?我接触到了3种方法DirectX9中的二维图片的加载就是其中之一,这里我们要讲的是第二种通过ID3DXSprite来实现DirectX 9.0C绘制2D动画

前一种方法我们说过了,是单纯的图片加入,对于游戏而言没有太大的用处,游戏里面毕竟动态的东西比较多,怎么让他动起来呢?我们先看一下源代码。

(本帖子不分析代码,主要留作讨论。代码分析在  [《快乐西游》模仿秀 ] 和 《超级马里奥.net游戏代码完全分析》中,欢迎下载)

以下代码在 Microsoft DirectX 9.0 SDK Update (August 2005)环境运行通过,如果遇到编译不通过在下面留言。

把SDK中例子NO.5(纹理)中的banana.bmp(或者自己找一个不要小于200*200像素的bmp文件)考到你的程序目录下。
(需要库文件d3d9.lib d3dx9.lib)

//--------------------------------程序2--------------------------------------

//                            学自网络       回馈网络
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
#include <d3dX9.h>

//-----------------------------------------------------------------------------
LPDIRECT3D9             g_pD3D       = NULL;
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVB        = NULL;
LPDIRECT3DTEXTURE9  g_pTexture  = NULL;
LPD3DXSPRITE   g_pSprite  = NULL;

struct CUSTOMVERTEX
{
    FLOAT x, y, z, rhw;
    DWORD color;      
};

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)


RECT rct;
 
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
 SetRect( &rct, 0,200, 100, 250 );

    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

   
    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &d3dpp, &g_pd3dDevice ) ) )
    {
        return E_FAIL;
    }

    g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

    g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );


 D3DXCreateSprite( g_pd3dDevice, &g_pSprite );

     if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, "banana.bmp", &g_pTexture ) ) )
    {
 
        if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, "..\\banana.bmp", &g_pTexture ) ) )
        {
            MessageBox(NULL, "Could not find banana.bmp", "Textures.exe", MB_OK);
            return E_FAIL;
        }
    }


    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
 g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
 g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
 g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
 g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
    return S_OK;
}


//-----------------------------------------------------------------------------
HRESULT InitVB()
{
    CUSTOMVERTEX vertices[] =
    {
        { 50.0f,  50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
        { 250.0f, 50.0f, 0.5f, 1.0f, 0xff00ff00, },
        { 50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
  { 250.0f, 250.0f, 0.5f,1.0f, 0xff00ffff, },
    };

    if( FAILED( g_pd3dDevice->CreateVertexBuffer( 4*sizeof(CUSTOMVERTEX),
                                                  0, D3DFVF_CUSTOMVERTEX,
                                                  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
    {
        return E_FAIL;
    }

  
    VOID* pVertices;
    if( FAILED( g_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 ) ) )
        return E_FAIL;
    memcpy( pVertices, vertices, sizeof(vertices) );
    g_pVB->Unlock();

    return S_OK;
}

//-----------------------------------------------------------------------------
VOID Cleanup()
{
    if( g_pVB != NULL )       
        g_pVB->Release();

    if( g_pd3dDevice != NULL )
        g_pd3dDevice->Release();

    if( g_pD3D != NULL )      
        g_pD3D->Release();
}

VOID Render()

{
 g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );


    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
  

  g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
        g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
        g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
 
  if ( SUCCEEDED( g_pSprite->Begin(D3DXSPRITE_ALPHABLEND) ) )
  {
   rct.right +=1;
   rct.left +=1;
   

   g_pSprite->Draw(g_pTexture, &rct, NULL, NULL, 0xffffffff);
   g_pSprite->End();
  }


      
        g_pd3dDevice->EndScene();
    }

  
    g_pd3dDevice->resent( NULL, NULL, NULL, NULL );
}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            Cleanup();
            PostQuitMessage( 0 );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
  
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      "D3D Tutorial", NULL };
    RegisterClassEx( &wc );

    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 02: Vertices",
                              WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
                              GetDesktopWindow(), NULL, wc.hInstance, NULL );


    if( SUCCEEDED( InitD3D( hWnd ) ) )
    {
       
        if( SUCCEEDED( InitVB() ) )
        {
      
            ShowWindow( hWnd, SW_SHOWDEFAULT );
            UpdateWindow( hWnd );

       
            MSG msg;
            ZeroMemory( &msg, sizeof(msg) );
            while( msg.message!=WM_QUIT )
            {
                if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
                {
                    TranslateMessage( &msg );
                    DispatchMessage( &msg );
                }
                else
                    Render();
            }
        }
    }

    UnregisterClass( "D3D Tutorial", wc.hInstance );
    return 0;
}

[此贴子已经被作者于2005-9-16 10:49:02编辑过]
发表于 2005-9-24 10:29:20 | 显示全部楼层
以下是引用鼯鼠在2005-9-1 17:01:24的发言:

2005 4月SDK版本不会产生位置错误,只是绘出来的可能比较模糊

&rct  是你选取纹理的位置,你可以在photoshopl里面仔细察看你准备绘制的纹理的位置。要把你的纹理画在什么地方是在pSprite->Draw()里面倒数第二个参数决定的。我上面给的是默认的位置,在屏幕的作上角。

还有一点,你的纹理尺寸必须是2的n次幂。

HRESULT Draw(      

    LPDIRECT3DTEXTURE9 pTexture,
    CONST RECT *pSrcRect,
    CONST D3DXVECTOR3 *pCenter,
    CONST D3DXVECTOR3 *pPosition,
    D3DCOLOR Color
);
其实,下面这两个参数是互相联动的,pCenter会根据pPosition的值进行自动调整,反之亦然,
所以,与其用这两个至同时控制旋转角度和显示位置,还不如放弃pPosition参数,
只用pCenter来控制旋转和缩放中心位置,显示位置用矩阵来变换。
反正我没能成功的同时控制那两个参数。
    CONST D3DXVECTOR3 *pCenter,
    CONST D3DXVECTOR3 *pPosition,
 
[此贴子已经被作者于2005-9-25 12:59:11编辑过]
 楼主| 发表于 2005-8-11 02:03:19 | 显示全部楼层

感谢Fenger提供的ID3DXSprite用法指导!!非常全面非常好!以下是引用Fenger的原话:

 

建立D3D和其他相关的设备应该没有问题吧.
  然后再定义一个ID3DXSprite*变量:
    ID3DXSprite* pSprite = NULL;

  然后在创建D3D设备(假如是pd3dDevice)之后, 用D3DXCreateSprite创建Sprite:
    D3DXCreateSprite( pd3dDevice, &pSprite );

  记得在设备丢失/释放时使用: SAFE_RELEASE( pSprite );

  在渲染场景的时候, 使用: 
    pSprite->Begin(x);
    ... // 具体绘制代码
    pSprite->End();

  其中的x可以是下面各值的组合, 如 D3DXSPRITE_ALPHABLEND | D3DXSPRITE_OBJECTSPACE
    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时推荐使用
  一般就是D3DXSPRITE_ALPHABLEND, 或者根据需要再加上D3DXSPRITE_OBJECTSPACE, 其他的我都不怎么用.....
  中间的绘制代码使用 pSprite->Draw, 函数原型为:
  HRESULT Draw(
    LPDIRECT3DTEXTURE9 pTexture,
    CONST RECT *pSrcRect,
    CONST D3DXVECTOR3 *pCenter,
    CONST D3DXVECTOR3 *pPosition,
    D3DCOLOR Color );
  pTexture 是需要绘制的贴图
  pSrcRect 是需要绘制的贴图上的一个矩形区域, 绘制整过贴图可以指定为NULL.
  pCenter 是绘制的中心座标(旋转时会以此点为中心), 指定NULL表示中心座标为(0,0,0)
  pPosition 是绘制的位置座标, 也可以指定NULL表示(0,0,0)
  Color 是绘制的颜色, 一般情况下指定为 0xffffffff. 最高位到底位的各8字节为Alpha, 红, 绿, 蓝, 如果指定0x80ffffff就是半透明贴图. 如果0xffff0000就只保留贴图里的红色分量, 具体功能自己体会. 当然贴图本身可以包含Alpha信息.

  需要旋转等功能可以使用 pSprite->SetTransform(), 函数原型为:
  HRESULT SetTransform( CONST D3DXMATRIX *pTransform );
  相信这个不用解释都很清楚了吧.

  最后再提醒一点: pSprite->Begin和pSprite->End 必须成对的出现在 IDirect3DDevice9::BeginScene 和 IDirect3DDevice9::EndScene 之间.
  还有问题加QQ: 103226172. 指明原因(验证消息).

发表于 2005-8-11 22:38:21 | 显示全部楼层
好~支持,有时间再认真参详一下!
发表于 2005-8-24 11:42:02 | 显示全部楼层
################
 楼主| 发表于 2005-8-24 20:54:55 | 显示全部楼层
以下是引用sdliubo在2005-8-24 11:42:02的发言:

我这里怎么会提示

error C2065: 'D3DXSPRITE_ALPHABLEND' : undeclared identifier

还有说draw函数的参数不是5个???

我已经说过了,这个程序是在DirectX 9.0C的SDK里编译通过的,在以前的版本中,draw函数有7个参数,你确定你的SDK版本是9.0c的吗?我给你写一个有7个参数的源代码,你可以运行一下。

================================================

if ( SUCCEEDED( g_pSprite->Begin() ) )
  {
   rct.right +=1;
   rct.left +=1;
   g_pSprite->Draw(g_pTexture, &rct, NULL,NULL,NULL, NULL, 0xffffffff);
   g_pSprite->End();
  }

==========================================

你可以尝试运行以下,你的sdk版本肯定不是9.0c的。

[此贴子已经被作者于2005-9-1 17:07:17编辑过]
发表于 2005-8-25 08:44:24 | 显示全部楼层
################
发表于 2005-8-31 14:22:05 | 显示全部楼层
################
 楼主| 发表于 2005-9-1 17:01:24 | 显示全部楼层

2005 4月SDK版本不会产生位置错误,只是绘出来的可能比较模糊

&rct  是你选取纹理的位置,你可以在photoshopl里面仔细察看你准备绘制的纹理的位置。要把你的纹理画在什么地方是在pSprite->Draw()里面倒数第二个参数决定的。我上面给的是默认的位置,在屏幕的作上角。

还有一点,你的纹理尺寸必须是2的n次幂。

发表于 2005-9-2 17:00:56 | 显示全部楼层
以后一定好好看文档先。很满意的回复。非常感谢!
发表于 2005-9-5 15:00:13 | 显示全部楼层
################
 楼主| 发表于 2005-9-5 18:00:15 | 显示全部楼层
以下是引用985ch在2005-9-5 15:00:13的发言:
我用的是SDK版本是9.0b的,因此用的是楼主在5楼的代码来绘制图片,但是我发现绘制出来的图片大小和原来的不同了……为什么?

你看看你的图片尺寸是不是2的n次幂。也就是是不是64;128;256这样的尺寸

发表于 2006-2-14 05:09:21 | 显示全部楼层

如果图片不是2的n次幂就会发生图像大小失真吗?

我现在就遇到了这样的情况,原来是这个原因

难道我必须将图片改称这样的大小吗?像128*16这样行吗

还有,题目说是2D动画,这段是实现动画的吗?

 楼主| 发表于 2006-2-14 09:47:52 | 显示全部楼层
以下是引用singohgod在2006-2-14 5:09:21的发言:

如果图片不是2的n次幂就会发生图像大小失真吗?

我现在就遇到了这样的情况,原来是这个原因

难道我必须将图片改称这样的大小吗?像128*16这样行吗

还有,题目说是2D动画,这段是实现动画的吗?

没错,图片不是2的n次幂就会发生图像大小失真,像128*16这样不行,必须是8*8 64*64 256*256

但是不是绝对的,你实在需要用不是2的n次幂就图像的话,在论坛里有解决的办法,有个参数设,更改以后就可以是任意尺寸了。

这个程序你运行一下就会知道他是不是动画了,所谓的动画,就是可以变化的图片,没错吧;至于图片是什么,怎么显示则是你写程序的方法了
发表于 2007-4-19 17:49:38 | 显示全部楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2024-5-21 21:58

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表