bussorenre Laboratory

hoge piyo foo bar

DirectX10 のTutorialを進めていく01

というわけで、今日はチュートリアルの01をやっていきます。

未だに世の中のゲームの主流はDX9ですが、windows7もだいぶ浸透してきたので、DirectX10くらいなら普通に動作するパソコンも、Dx10リリースの4年前と比べると圧倒的に増えました。


さて、そんなわけで、DX11に移行するのも楽なので、Dx10を進めていきます。プログラマー的にDX10は楽ですね。高機能が保障されてて。


また、今回紹介する内容は、DirectX HelpのTutorial01を俺用に日本語訳したものとほぼ同等です。詳しくは、(英語ですが)Helpを見てください。



今回は、Direct3D10の初期化を行います。

DirectXすべてにおいてそうですが、使用する前にはデバイスを初期化する。これ鉄則です。3Dを使うためのデバイスをプログラムの中で用意してやる必要があります。


環境はVisual Studio 2005以降を想定してます。

流れとしては、

  • 窓を作る(前回軽くやりました)
  • 初期化情報を書き込む
  • Direct3Dデバイス(とスワップチェーン)を作成
  • RenderTargetViewを作成

この4段階です。
ここまで出来上がって初めてグラフィックを描画できます。めんどくさいですね(ぁ

初期化情報の設定

というわけで、窓は前回作ったので省略。デバイスの初期化情報を書き込みます。

    DXGI_SWAP_CHAIN_DESC        sd;                     // スワップチェーン構造体
	//DXGI_SWAP_CHAIN_DESCのパラメーターの設定
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 1;                                 // バックバッファの数(通常は1)
    sd.BufferDesc.Width = nWidth;		                // バックバッファの横幅(通常はクライアント領域の横幅)
    sd.BufferDesc.Height = nHeight;		                // バックバッファの縦幅(通常はクライアント領域の縦幅)
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;  // バッファのフォーマット
    sd.BufferDesc.RefreshRate.Numerator = 60;           // リフレッシュレートの分子(通常は60)
    sd.BufferDesc.RefreshRate.Denominator = 1;          // リフレッシュレートの分母(通常は1)
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;   // レンダーターゲットの出力を使用
    sd.SampleDesc.Count = 1;                            // 通常は1
    sd.SampleDesc.Quality = 0;                          // 通常は0
    sd.Windowed = bWindowed;                            // ウィンドウモードならtrue

	sd.OutputWindow = hWnd;                           // 作成先デバイスのウィンドウハンドル

DirectX9の時代は、デバイスが必ず一つのスワップチェーン(swapchain)を持っていましたが、DX10以降はデバイスとスワップチェーンは独立しています。

さて、そもそもスワップチェーンって何やねんって話ですが、DirectXはグラフィックを描画するために、メモリバッファを最低二つ持っています。FrontバッファとBackバッファです。

f:id:bussorenre:20110204223057p:image

frontバッファは画面に表示されるバッファです。こちらが画面に表示されている間に、プログラムはBackバッファにグラフィックを書き込んでいきます。で、描画が完了すると、frontバッファとbackバッファを入れ替えて、画面を更新します。

Q.なんでこんな回りくどい方法とっているの?直接Frontバッファに書き込めばよくね?
f:id:bussorenre:20110204223058p:image
直接frontバッファに描画していくと、描画処理途中であろうがディスプレイに現在の描画状態が反映されてしまうため、チラつきが発生します。それを回避するためにこのような回りくどい方法をとっています。

上記のソースコードで特筆すべき部分はsd.Windowedでしょうかね。falseにするとフルスクリーンモードになります。また、画素の設定ですが、デフォルトの設定を入れました。他にも色々なフォーマットがあるのですが、あまり使うこともないので興味があればggってください。その他はコメントに記したとおり。

デバイスをスワップチェーンの作成

さて、スワップチェーンの情報を構造体に書き込んだところで、次はデバイスとスワップチェーンを同時に作成します。

    // デバイスの作成
    ID3D10Device*               m_pd3dDevice;           // Direct3D10 Device オブジェクト
    IDXGISwapChain*             m_pSwapChain;           // スワップチェーンオブジェクト

    HRESULT        hr;
    hr = D3D10CreateDeviceAndSwapChain(
        NULL,                                           // 通常はNULLを指定(詳しいアダプタ設定をする)
        D3D10_DRIVER_TYPE_HARDWARE,                     // ハードウェアモードで起動
        NULL,                                           // 通常はNULL
        0,                                              // 通常は0.それ以外はろくなことがない
        D3D10_SDK_VERSION,                              // DirectX SDK のバージョン
        &sd,                                            // DXGI_SWAP_CHAIN_DESC構造体のポインタ
        &m_pSwapChain,                                  // 作成先スワップチェーン
        &m_pd3dDevice );                                // 作成先デバイス

    if(FAILED(hr)){
        hr = D3D10CreateDeviceAndSwapChain(
            NULL,                                       // 通常はNULLを指定(詳しいアダプタ設定をする)
            D3D10_DRIVER_TYPE_REFERENCE,                // リファレンスモードで起動
            NULL,                                       // 通常はNULL
            0,                                          // 通常は0.それ以外はろくなことがない
            D3D10_SDK_VERSION,                          // DirectX SDK のバージョン
            &sd,                                        // DXGI_SWAP_CHAIN_DESC構造体のポインタ
            &m_pSwapChain,                              // 作成先スワップチェーン
            &m_pd3dDevice );                            // 作成先デバイス
        if(FAILED(hr)){
            return hr;
        }
    }

Direct3Dには二つの起動モードがあります。ハードウェアモードとリファレンスモードです。ハードウェアモードは、グラフィック処理をGPU(3Dチップ)に任せる方式で、ふつうはこれを使いますが、GPUがDX10に対応していないとリファレンスモード、CPUがすべての処理を行う方式で起動します。ただ、この方式は物凄く重いので推奨しません。デバッグ時以外は使用するべきじゃありません。

第1引数のアダプタ指定ですが、ディスプレイが複数あったり、グラフィックカードが複数刺さっている時など、細かい指定を行う場合に使いますが、特に設定しない場合はデフォルトモニターが選択されます。

HRESULT型は、DirectXのエラー処理に使われる変数の型です。結構よく使うので覚えておきましょう(笑)

RenderTargetView

最後に、RenderTargetViewを作成します。これが最もわかりにくい概念で、初心者殺しの最初の壁といわれています。

DirectX10は、テクスチャや頂点バッファや形状データ等のあらゆるリソース(直訳で資源。後述)とパイプライン(描画するための機構。後述)をつなげるためにViewを作成しなければならないことになっています。トンネルみたいなものでしょうか。

RenderTargetViewはデバイスとスワップチェーンをつなげます。かなりめんどくさいですね。初期化時に自動的につなげてくれよと(笑)

    ID3D10RenderTargetView*     m_pRenderTargetView;    // 描画ターゲットビュー
    ID3D10Texture2D             *pBackBuffer;           // バックバッファ取得用テクスチャ

    // バックバッファを取得(2Dテクスチャで取得)
    hr = m_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), (LPVOID*)&pBackBuffer );
    if( FAILED(hr) )
        return hr;

    // レンダーターゲットビューの作成
    hr = m_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &m_pRenderTargetView );
    pBackBuffer->Release();
    if( FAILED(hr) )
        return hr;

    // 描画ターゲットにさっき作ったビューをセットする
    m_pd3dDevice->OMSetRenderTargets( 1, &m_pRenderTargetView, NULL );

なんで途中でテクスチャ取得してるの?これは実は私もよくわかってません。なんでテクスチャ取得しないとだめなんでしょうね??ただこうしないと動かないのでこうします。


というわけで、以上で初期化処理が終了になります。お疲れ様でした。あ、そうそう、DirectXで作成したオブジェクトは、使い終わったら全部解放してやらないといけません。

#define SAFE_RELEASE(p)      { if (p) { (p)->Release(); (p)=NULL; } }

    SAFE_RELEASE(m_pRenderTargetView);
    SAFE_RELEASE(m_pSwapChain);
    SAFE_RELEASE(m_pd3dDevice);

マクロ定義してるのは、簡略化のためです笑

これを最後に書かないと、メモリ・GPUリソースが徐々に食われていくので注意。



サンプルプログラムは、DirectX Sample Browserから落としてください。(・∀・)