2009年03月04日

DirectXで2Dスプライト表示


DirectXというとポリゴンをふんだんに使った3Dプログラミングや動画再生など
どちらかというとマルチメディア処理を行うためのものと言う印象が強いかと思います。
当然その認識は間違っておらずDirectXライブラリを使用することで処理のハードウェアの支援や
プログラムの簡素化など非常に役に立つライブラリです。

そんなDirectXですが当然マルチメディアというからには3Dだけではなく2D処理も得意で
2D画像の表示や回転・移動などの処理を行う際にも絶大な威力を発揮します。
今回はDirectXを使用して2D画像の扱い方を説明します。

そもそもスプライトとは何?

まず、DirectXでのスプライトとはどんなものかを説明します。

ビルボードという概念があります。
このビルボードとは3D空間上に存在する表裏のある板なんですが この板は見ている人に対して常に正面を向くという特徴を持っています。

つまりカメラをどの場所に移動してその板を見ても常にカメラを正面にして向きを変える板です。

こういう特徴を持った板をビルボードと呼び、さらにDirectXではスプライトと呼んでいます。

当然ポリゴンを作成してカメラとの位置を計算して自分のほうに向きを変更するなどの処理を実装して 自分でビルボードを作成することもできるのですが DirectXのスプライトはこの辺の処理を上手に隠蔽してくれているのです。

ここではそのスプライトの使い方を解説していきます。

スプライトの表示の流れ

プログラムの流れは次の通り。ポリゴンなどを使った普通のDirectX、3Dプログラミングと同じですね。
処理と主に使うAPIを並べておきます。

  1. Direct3Dのデバイスを初期化する
    Direct3DCreate9()
    IDirect3D9::GetAdapterDisplayMode()
    IDirect3D9::CreateDevice()
    IDirect3DSurface9::GetDesc()

  2. スプライトを作成する
    D3DXCreateSprite()
    D3DXCreateTextureFromFile()
    IDirect3DTexture9::GetSurfaceLevel()

  3. 作成したスプライトを表示用に変換する
    D3DXMatrixIdentity()
    D3DXMatrixScaling()
    D3DXMatrixRotationZ()
    D3DXMatrixTranslation()

  4. 描画する
    IDirect3DDevice9::Clear()
    IDirect3DDevice9::BeginScene()
    ID3DXSprite::Begin()
    ID3DXSprite::Draw()
    ID3DXSprite::SetTransform()
    ID3DXSprite::End()
    IDirect3DDevice9::EndScene()
    IDirect3DDevice9::Present()

  5. 後処理
    IDirect3DTexture9::Release()
    ID3DXSprite::Release()
    IDirect3DDevice9::Release()
    IDirect3D9::Release()

それじゃ作成

上の流れに沿ってそれぞれコードを見ていきましょう。
細かいパラメータはハードコーディングしてありますがひとまず後回しにしときましょう。

Direct3Dのデバイスを初期化する
HWND hWnd;
HRESULT hr;
D3DPRESENT_PARAMETERS d3dpp;
D3DDISPLAYMODE        dmode;

UINT                  nAdapter;
D3DDEVTYPE            dwDeviceType;        // デバイスの種類
DWORD                 dwBehaviorFlags;     // デバイスの動作モード
D3DSWAPEFFECT         dwSwapEffect;

// パラメータの設定
dwDeviceType    = D3DDEVTYPE_HAL;
nAdapter        = D3DADAPTER_DEFAULT;
dwBehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
dwSwapEffect    = D3DSWAPEFFECT_DISCARD;

// D3Dオブジェクトの作成
pD3D = ::Direct3DCreate9(D3D_SDK_VERSION);

// 現在のディスプレイモードを取得
::ZeroMemory(&d3dpp, sizeof(d3dpp));
hr = pD3D->GetAdapterDisplayMode(nAdapter, &dmode);
if (FAILED(hr))
    return hr;

// パラメータの設定
d3dpp.Windowed               = TRUE;            // ウィンドウモードで表示
d3dpp.SwapEffect             = dwSwapEffect;
d3dpp.BackBufferFormat       = dmode.Format;
d3dpp.BackBufferCount        = 2;               // トリプルバッファリング
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;      // 16Bit Zバッファ作成

// IDirect3DDevice9オブジェクトの作成
hr = pD3D->CreateDevice(nAdapter, dwDeviceType,
            hWnd, dwBehaviorFlags, &d3dpp, &pd3dDevice);
if (FAILED(hr))
    return hr;

return S_OK;

スプライトとテクスチャの作成を行います。
ここでは画像ファイルからデータを読み込みテクスチャを作成します。

スプライトを作成する
HRESULT hr = S_FALSE;
UINT uLevel = 0;
IDirect3DSurface9* pSurface = NULL;
LPCTSTR lpszFilename = _T("texture.png");

// スプライト情報の生成
if (SUCCEEDED(hr))
    hr = ::D3DXCreateSprite(pd3dDevice, &pSprite);
if (SUCCEEDED(hr))
    hr = ::D3DXCreateTextureFromFile(pd3dDevice, lpszFilename, &pTexture);
if (SUCCEEDED(hr))
    hr = pTexture->GetSurfaceLevel(uLevel, &pSurface);

// サーフェイス取得
if (SUCCEEDED(hr))
    hr = pSurface->GetDesc(&sSurfaceInfo);
if (pSurface != NULL)
    pSurface->Release();
pSurface = NULL;

ここでは描画するウィンドウ上にどのようにスプライトを変換するかを記述します。
具体的には移動、回転、拡大縮小などの処理を記述することになります。
ここでは読み込んだ画像をウィンドウ全体に表示することにします。

作成したスプライトを表示用に変換する
RECT rc;
FLOAT x, y;

// 変換行列初期化
::D3DXMatrixIdentity(&mat);

// ウィンドウサイズにスプライトの大きさをあわせる
::GetWindowRect(hWnd, &rc);
x = (FLOAT)(rc.right - rc.left) / sSurfaceInfo.Width;
y = (FLOAT)(rc.bottom - rc.top) / sSurfaceInfo.Height;

// 拡縮させる
::D3DXMatrixScaling(&mat, x, y, 0.0f);

作成・変換したスプライトをウィンドウに表示します。
WM_PAINTメッセージやOnOaint()イベントハンドラで呼ぶか
ゲームなどだとループを作成して固定FPSで描画ルーチンを呼ぶことになります。

描画する
// パラメータの設定
D3DXVECTOR3 vCenter     = D3DXVECTOR3(0.0f,0.0f,0.0f);
D3DCOLOR    clrAlpha    = D3DCOLOR_RGBA(0xff,0xff,0xff,0xff);
D3DCOLOR    clrBgColor  = D3DCOLOR_RGBA(0xff,0xff,0xff,0xff);

// クリア
pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
        clrBackgroundColor, 1.0f, 0);

// 描画開始
if (SUCCEEDED(pd3dDevice->BeginScene())) {

    // スプライトを描画
    pSprite->Begin(D3DXSPRITE_ALPHABLEND);
    pSprite->Draw(
        pTexture,      // LPDIRECT3DTEXTURE9 pTexture
        NULL,          // CONST RECT *pSrcRect
        &vCenter,      // CONST D3DXVECTOR3 *pCenter
        NULL,          // CONST D3DXVECTOR3 *pPosition
        clrAlpha       // D3DCOLOR Color
    );
    pSprite->SetTransform(&mat);
    pSprite->End();

    // シーン描画の終了
    pd3dDevice->EndScene();
}

// バッファを表示
pd3dDevice->Present(NULL, NULL, NULL, NULL);

アプリが終了したときはそれぞれ破棄します。
ま、お決まりですね。

後処理
// スプライトの破棄
if (pTexture != NULL)
    pTexture->Release();
pTexture = NULL;
if (pSprite != NULL)
    pSprite->Release();
pSprite = NULL;

// IDirect3DDevice9オブジェクトの解放
if (pd3dDevice != NULL)
    pd3dDevice->Release();
pd3dDevice = NULL;

// IDirect3D9オブジェクトの解放
if (pD3D != NULL)
    pD3D->Release();
pD3D = NULL;

このあたりをうまくまとめればウィンドウにスプライトが表示されます。

ソースコード・その他

上のコードではパラメータに直接ハードコーディングでパラメータ値を記述しています。
一つづつ説明するとかなり膨大な量になるのでリンクをご覧ください。




人気ブログランキングへ
posted by ひで at 13:48 | Comment(0) | TrackBack(0) | コンピュータ関連 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。