今回はDirectX11での画面のクリアとフリップについて記載していきます。
実装部分には前回の初期化の続きになるので、DirectX11クラスはそちらをご確認ください。
では早速みていきましょう!
SwapChain
前回の記事で生成した部分にはなりますが、DirectXにはスワップチェインと呼ばれるものがあります。
スワップチェインとは、画面の更新を制御する仕組みです。
例えば、60FPSで画面を更新するといった更新タイミングの制御等を行っています。
バックバッファ
スワップチェインは、フロントバッファとバックバッファの2つの絵を管理しています。
フロントバッファは名前の通り前に出ているバッファで実際の画面に表示されている内容となります。
バックバッファは裏に隠れている絵を指しており、ここに描画をしていくことで次のフレームに表示する絵を作り上げていきます。
この2つのバッファを交互に切り替えて画面をパラパラ漫画のように更新することでDirectXでは画面を動かしています。
SwapChainによる画面更新
今回はこのスワップチェインを利用した画面の更新を作成していきます。
スワップチェインではRenderTargetという描画するターゲット(画面)の情報を保持しています。
先ずはSwapChainからRenderTargetの情報を取り出す仕組みを実装してみましょう。
DirectX11.h
// DirectX11クラス class DirectX11 { public: DirectX11(); ~DirectX11(); // 初期化 bool Initialize(Window& window); // 解放 void Finalize(); public: // デバイスの取得 ID3D11Device* GetDevice() { return m_pDevice; } // デバイスコンテキストの取得 ID3D11DeviceContext* GetContext() { return m_pDeviceContext; } // スワップチェインの取得 IDXGISwapChain* GetSwapChain() { return m_pSwapChain; } // レンダーターゲットの取得 ID3D11RenderTargetView* GetRenderTargetView() { return m_pRenderTargetView; } private: private: ID3D11Device* m_pDevice; // DirectX11のデバイス ID3D11DeviceContext* m_pDeviceContext; // 描画用のデバイスコンテキスト IDXGISwapChain* m_pSwapChain; // 画面出力用のスワップチェイン ID3D11RenderTargetView* m_pRenderTargetView; // 画面出力用のレンダーターゲット };
DirectX11.cpp
// 初期化 bool DirectX11::Initialize(SimpleWindow& window) { // ~ ここまで前回の記事のコード ~ // レンダーバッファの取得 ID3D11Texture2D* pBuffer; if (FAILED(m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBuffer))) { Finalize(); return false; } // レンダーターゲットビューの生成 if (FAILED(m_pDevice->CreateRenderTargetView(pBuffer, NULL, &m_pRenderTargetView))) { Finalize(); return false; } SafeRelease(pBuffer); return true; } // 終了処理 void DirectX11::Finalize(void) { SafeRelease(m_pDevice); SafeRelease(m_pDeviceContext); SafeRelease(m_pSwapChain); SafeRelease(m_pRenderTargetView); }
太字になっている箇所が追加部分になります。
これで画面の内容を管理するRenderTargetViewが取得できるようになりました。
今回出て来たものは以下の2つとなります。
ID3D11Texture2D
2Dの絵を管理するためのオブジェクトです。
SwapChainから取り出した場合は画面に表示する内容を管理するTextureを取り出す事が出来ます。
ID3D11RenderTargetView
ID3D11Texture2DからCreateRenderTargetViewというものを生成しています。
これは画面に出力する際に必要なオブジェクトとなります。
DirectX11で描画をする際には必ずRenderTargetViewを介して描画する形になります。
画面のクリア
前項ではRenderTargetViewの生成する処理の実装を記述しました。
次はRenderTargetViewを使って画面を任意の色に塗り潰してみましょう!
DirectX11.h
// DirectX11クラス class DirectX11 { public: DirectX11(); ~DirectX11(); // 初期化 bool Initialize(Window& window); // 解放 void Finalize(); // 画面のクリア void ClearTarget(float r, float g, float b); // 画面のフリップ void Present(); // ~ 以降は前回までのコード ~
DirectX11.cpp
// 画面のクリア void DirectX11::ClearTarget(float r, float g, float b) { const float color[4]{ r, g, b, 1.0f }; m_pDeviceContext->ClearRenderTargetView(m_pRenderTargetView, color); } // 画面のフリップ void DirectX11::Present() { m_pSwapChain->Present(1, 0); }
次に画面のクリアとフリップ処理を実装してみました。
実装しただけでは動かないので実際に使ってどうなるか確認してみましょう。
main.cpp
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // ~ ここまで前回の記事のコード ~ // メインループ while (!window.IsQuitMessage()) { // メッセージの更新 if (!window.UpdateMessage()) { // 青色に画面をクリアする directX.ClearTarget(0, 0, 1); // 画面の内容をフリップする directX.Present(); } } directX.Finalize(); return 0; }
Windowの作成で作ったmain.cppのメインループに組み込んでみました。
これで画面のクリアとフリップが実行されたはずです!
実際に実行して画面の内容を見てみると…
画面が真っ青になりました。
これはdirectX.ClearTarget(0, 0, 1)でr, g, bを0, 0, 1と設定しているからです。
試しに数値の配分を自由に変えてみましょう。
色が変わってプログラムが正しく動いているのが分かると思います。
では次にdirectX.Present()をコメントアウトしてみるとどうなるでしょうか?
何と画面の色が変わらなくなります。
何故、Presentを呼ぶのをやめると色が変わらなくなるのでしょうか?
画面のクリアとフリップとは?
前項でClearTargetで色を指定してPresentを呼び出すと色が変わる事が分かりました。
ではClearTargetとPresentはそれぞれ何をしているのでしょうか?
ClearTarget
RenderTargetの色を指定した色で塗りつぶす処理です。
画面の色が青や指定した色に変わりましたよね?
なのでそのままの意味で画面の色をクリアする機能となっています。
Preset
ClearTargetと違ってパッと何をしているのか分かり難いのがPresentです。
実はPresentはRenderTargetに描かれた絵とモニタに表示された絵を差し替える機能を持っています。
例えば、美術室で絵を描いている時に先生にのぞき込まれたら中途半端な絵が見えてしまいますよね。
だから書き終わったら見せるという形にPresentが呼び出されるまでは画面に絵が出ないようにしています。
この記事のタイトルにフリップと書いてありましたが、フリップと反転を意味しています。
なのでモニタの絵と描き終わった絵を反転させるといった機能がPresentになります。
これで基本の画面を制御する仕組みは終わりです。
やる事自体は簡単ですよね!
こういった細々とした制御を行ってゲームの描画は行われています。