Back Buffer を取得して円を描画する

DirectX の GetBackBuffer で Buffer を取得して円を描画する Direct3D9 の最も簡単なプログラムです。
D3D Device が正常に取得できればページ先頭の画像を表示することができます。

前田稔(Maeda Minoru)の超初心者のプログラム入門

DirectX の参考書に添付されているライブラリに頼っておられる方を良く見かけますが、初心者の内は良いとしてもゲームプログラマとしては情けない限りです。
肝心な所がブラックボックスのままでは、冗談にもなりません。
Direct3D のプログラムで最初に苦労するのが Direct3D Device の取得です。
DirectX 3D Graphics のプログラムを組むからには「少なくともこのハードルをクリア」していただきたいものです。

Direct3D の実行環境

DirectAudio のハードウエア(サウンドカード)は、提供している会社も少なく比較的互換性も保たれています。
代表的な会社は次の二社でしょう。
Sound Blaster
YAMAHA

Direct3D の実行環境は DirectAudio と違って多種多様です。
一番の問題はビデオカードまたは、アクセラレータボードなどと呼ばれているグラフィックスを描画するハードウエアで、 規格が統一されていない上に多くのメーカが機能を競って独自に開発を続けてきました。
DirectX の目的の一つはハードに依存しない統一したプログラム環境を提供することなのですが、もう一方でハードウエアの 機能を最大限に生かしてゲームのような非常に速い描画速度を要求するプログラムにも対応することなのです。
ビデオカード以外にもモニタの規格や描画専用のバスなどハードウエアの多くの違いを克服しなければなりません。

Direct3D のプログラムを作成する上で、この影響をもろに受けるのが Direct3D Device の取得です。
Direct3D Object の取得は簡単なのですが Direct3D Device の取得は苦労の連続です。
苦労してうまく取得できても、マシンが変わったりプログラミング手法が変わるたびに悩まされ続けてきました。
  LPDIRECT3D9             g_pD3D= NULL;
  LPDIRECT3DDEVICE9       g_pDEV= NULL;
D3D の最初の課題として、Direct3D Device を取得しているソースコードを探し出して、このプログラムに組み込んで下さい。
Direct Audio でトレーニングはできていると思いますが、教科書やサンプルやネットなどを参考にして下さい。

プロジェクトの作成

  1. 空の新規プロジェクト(Backbuf)を作成して、ファイルをフォルダーに格納して下さい。
    追加するファイルは Buckbuf.cpp だけです。
    ファイル名 ファイルの説明
    Backbuf.vcproj プロジェクトファイル
    Backbuf.cpp ソースプログラム
  2. Backbuf.cpp のソースコードです。
    g_pD3D が Direct3D Object で、g_pDEV が問題の Direct3D Device の定義です。
    g_pBackBuffer は円を描画するための Surface です。
        /*********************************************************/
        /*★ BackBuffer を取得して、円を直接描画する   前田 稔 ★*/
        /*********************************************************/
        #define     NAME    "Back Buffer Ellipse"
        #include    <Windows.h>
        #include    <d3dx9.h>
        #define     SAFE_RELEASE(x) if(x){x->Release();x=NULL;} 
    
        #pragma once
        #pragma comment(lib,"dxerr9.lib")
        #pragma comment(lib,"dxguid.lib")
        #pragma comment(lib,"d3d9.lib")
        #pragma comment(lib,"d3dx9.lib")
    
        // Global Area
        HWND                    g_hWnd;
        LPDIRECT3D9             g_pD3D = NULL;          // D3DDeviceを作成するために使用
        LPDIRECT3DDEVICE9       g_pDEV = NULL;          // 描写用デバイス
        LPDIRECT3DSURFACE9      g_pBackBuffer = NULL;   // Back Buffer Surface
        bool                    g_bActive = false;      // アクティブ状態
        D3DPRESENT_PARAMETERS   g_D3DPP;                // Device 設定パラメータ
        
  3. WinMain() 関数は、2Dグラフィックと同じなので説明の必要は無いでしょう。
    メッセージループに入る前に InitDXGraphics() でプログラム全体の初期化を行っています。
    レンダリング関数 Draw() は、メッセージループから直接呼び出しています。
    今回のような動きの無い(動きが少ない)場合は WM_PAINT: から呼び出すことも出来ます。
        //★ WinMain()
        int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int)
        {   MSG         msg;
            HRESULT     hr;
    
                :
    
            // DirectX Graphicsの初期化
            hr = InitDXGraphics();
            if (FAILED(hr))
            {   MessageBox(NULL,"WinMain InitDXGraphics","Error Message", MB_OK);
                UnregisterClass(NAME, hInst);
                return FALSE;
            }
            // ウインドウ表示
            ShowWindow(g_hWnd, SW_SHOWNORMAL);
            UpdateWindow(g_hWnd);
            // メッセージ・ループ
            do
            {   if (PeekMessage(&msg,0,0,0,PM_REMOVE))
                {   TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else
                {   // レンダリングの処理
                    Draw();
                }
            } while(msg.message != WM_QUIT);
            UnregisterClass(NAME, hInst);
            return msg.wParam;
        }
        
  4. CALLBACK 関数も2Dグラフィックと同じなので説明の必要は無いでしょう。
    WinMain() から Draw() 関数でレンダリングしているので Escape キーの検出と終了処理だけです。
        // CALLBACK 関数
        LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam)
        {
            switch(msg)
            {   case WM_ACTIVATE:
                    g_bActive = (LOWORD(wParam) != 0);
                    break;
                case WM_KEYDOWN:
                    switch(wParam)
                    {   case VK_ESCAPE:
                            PostMessage(hWnd,WM_CLOSE,0,0);
                            break;
                    }
                    break;
                case WM_DESTROY:
                    CleanupDXGraphics();
                    PostQuitMessage(0);
                    g_hWnd = NULL;
                    return 0;
            }
            return DefWindowProc(hWnd, msg, wParam, lParam);
        }
        
  5. WinMain() から呼ばれる InitDXGraphics() です。
    Init3DDev() がこれから作成していただく問題の Direct3D Device を取得する関数です。
    関数の中で g_pD3D と g_pDEV を取得するソースコードを記述して下さい。
    取得の方法は、ページ後部のヒントを参照して下さい。
    Device が取得できたら、次に BackBuffer を取得します。
        HRESULT InitDXGraphics(void)
        {   HRESULT  hr;
    
            // Direct3D デバイスの設定
            if (FAILED(Init3DDev()))    return E_FAIL;
            //☆Back Buffer を取得
            hr= g_pDEV->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&g_pBackBuffer);
            if (FAILED(hr))
            {   MessageBox(NULL,"GetBackBuffer Error","Error Message", MB_OK);
                return E_FAIL;
            }
            return S_OK;
        }
        
  6. 円を描画する関数です。
    Back Buffer の HDC を取得して Ellipse() で円を描画しています。
    HDC の取得には制限があり、どんな状態でも取得できる訳ではありません。
    詳細は Microsoft のドキュメント(DirectX9_c.chm) の IDirect3DSurface9::GetDC メソッドを参照して下さい。
    取得した HDC は ReleaseDC() で開放して下さい。
    g_pDEV->Present() で作成したシーンを描画します。
        HRESULT Draw(void)
        {   HDC         hDC;
            HRESULT     hr;
    
            if (!g_pDEV)        return false;
            if (!g_bActive)     return true;
            g_pDEV->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(100,100,50),1.0f,0);
            if (g_pBackBuffer!=NULL)
            {   //☆Bach Buffer の HDC を取得して直接描画
                hr= g_pBackBuffer->GetDC(&hDC);
                if (!FAILED(hr))
                {   Ellipse(hDC,50,50,350,350);
                    g_pBackBuffer->ReleaseDC(hDC);
                }
                else    MessageBox(NULL,"GetDC Error","Error Message", MB_OK);
            }
            //シーンの表示
            return g_pDEV->Present(NULL,NULL,NULL,NULL);
        }
        
  7. プログラムの終了処理です。
    取得した Object を全て開放して下さい。
        bool CleanupDXGraphics(void)
        {   SAFE_RELEASE(g_pBackBuffer);
            SAFE_RELEASE(g_pDEV);
            SAFE_RELEASE(g_pD3D);
            return TRUE;
        }
        
  8. 2011/12/09 に Windows7 & June2010 & VS.NET2005 の環境で動かしてみました。
    "dxerr9.lib" が見つからないのエラーが表示されたので、次の行を削除します。
    //#pragma comment(lib,"dxerr9.lib")
    無事実行を確認しました。

【ヒント】

Direct3D Object と Direct3D Device の取得です。
ゲームプログラマを目指して DirectX を使うからには、これぐらいの課題は何としてもクリアしたいものです。
  1. Microsoft のドキュメント(DirectX9_c.chm)で調べるときは IDIRECT3D9 と IDIRECT3DDEVICE9 で検索して下さい。
      LPDIRECT3D9             g_pD3D= NULL;
      LPDIRECT3DDEVICE9       g_pDEV= NULL;
        
  2. Direct3D Object は、次の関数で簡単に取得できます。
    取得に失敗すると NULL がリターンされます。
    Direct3DCreate9()
  3. Direct3D Device は、g_pD3D から次の関数で取得するのですが、苦労の連続です。
    取得に失敗するとリターンコードが FAILED になります。
    g_pD3D->CreateDevice()
  4. DirectX の Sprite でも Direct3D Device を取得しているので、これを参照するのが簡単でしょうか?
  5. 一口に Device の取得と言っても方法は様々で、長いソースコードから簡単な物まで方法は幾通りもあります。
    Caps(Capability Bits Tests) とは、その GPU がどんな機能を持っているかを調べる仕組みですが、 Caps を調べて出来るだけ多くのハードウエアに対応しようと、数百行をこえる物もあります。
    ハードの仕様を調べて設定を限定すると、非常に短いステップ数で取得することが出来ます。
    私もハードウエアと用途によって使い分けていますが、今回使用したソースコードは約30行です。
  6. このプログラムは Direct3D を使っていますが、3DGraphics を描画している訳ではありません。
    3DGraphics を描画する最も簡単なプログラムは D3D で三角形を描画する を参照して下さい。
  7. DirectX の日本語 Help(DirectX9_c.chm) の中にチュートリアルが記述されています。
        DirectX Graphics
          プログラミングガイド
            チュートリアル、サンプル、・・・
              チュートリアル
                チュートリアル1:デバイスの作成
                      :
                チュートリアル6:メッシュの使い方
        
  8. チュートリアルのプログラムは次のフォルダーに格納されています。
    チュートリアルのプログラムは「ハード構成」によっては動かない場合があります。
    プログラムが動かないときは、ハードウエアに合わせてデバイスの設定を行って下さい。
    C:\DXSDK\Samples\C++\Direct3D\Tutorials\

【HAL&HEL】

HAL(Hardware Adstruction Layer)とは DirectX の描画機能をハードウエアで行う設定で高速で描画することが出来ます。
HEL(Hardware Emulation Layer)とは HAL が使えないときにソフトウエアで代替する機能で、速度は劣りますがプログラムを実行することは出来ます。

WindowsXP では、デスクトップのプロパティから HAL と HEL の設定を確認して下さい。
[設定][詳細設定][トラブルシューティング] を表示します。
スライダを操作してハードウェアアクセラレータのレベルを設定します。
LigthWave(旧バージョン?) では設定を[なし]にしなければ描画出来ませんでした。

スライダを操作して設定を[最大]にして下さい。
DirectX のゲームプログラムは、様々なプラットホームで実行されます。
少なくともスライダの設定を「なし」でも「最大」でも動くようにすることは最低限必要です。

【MEMO】

Direct3D10 からは Caps の仕組みが廃止され、デバイスの取得も簡単になりました。
ビデオボードが高機能化し、仕様が徐々に統一されてきたからでしょうか。
詳細は Windows Guid を参照して下さい。

デバイスの取得が簡単になったのは有り難いのですが、その代わりビデオボードが対応していないとプログラムが動きません。
DirectX10 以降の3Dプログラムを動かすには、それに対応した専用のボードが必要です。 (^_^;)

超初心者のプログラム入門(DirectX9 game program)