ここからはDirectX11を使ったプログラミングになる。
(DirectXとは?)
DirectX11で画面を表示するためには、60分の1秒の間に次のような処理を行う。
デバイスコンテキストという絵描きが絵を描く。
描いた絵は、レンダーターゲットビューというプリンタを介してレンダーターゲット(バックバッファ)という紙に出力される。
出力された画像はスワップチェインという者によって、ビューポートという場所に貼り付けられる。
これらの作業はデバイスという監督によって管理されている
上の説明はもちろん概念的なもの。
もうちょっと真面目に説明すると以下のような感じ。
ビデオカード(グラフィックボード)そのものを扱うクラス。
Direct3Dそのものと言っていい仕組みで、レンダリングに必要な各工程の総体。
実際に描画処理を行うオブジェクト。
描画そのもののコマンドをまとめたクラス。
現在画面に表示されてる絵をフロントバッファという。
それとは別にバックバッファという画用紙を複数用意することができる。
まずは画面に映ってないバックバッファの方に絵を描き、完成したらフロントバッファとバックバッファを交換(スワップ)する。
この仕組みのおかげで、画面がちらつくことなくアニメーションを表示できる。
この仕事をするのがスワップチェイン。
デバイスコンテキストが絵を描く画用紙。
スワップチェインが持つ複数のバックバッファの中で、今描画しようとしているもの。
DirectX11用語で「○○ビュー」と言ったら「○○と関連付け付けるもの」「○○と橋渡しするもの」みたいな意味。
レンダーターゲットビューは、レンダーターゲットビュー(今絵を描こうとしている画用紙)にアクセスするためのものになる。
ゲーム画面を表示する位置。
普通はウィンドウの中を全部ビューポートにする。
ということで、これらのメンツを揃えないとDirectX11での描画ができない。
まず、DirectXを使ったプログラムには次のコードが必須になる。
//インクルード
#include <Windows.h>
#include <d3d11.h>
//リンカ
#pragma comment(lib, "d3d11.lib")
これで、このプログラムでDirect3Dの各機能が使えるようになる。
次に、上で説明したメンツを扱うのに必要な変数を用意する。
//プロトタイプ宣言
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
ID3D11Device* pDevice; //デバイス
ID3D11DeviceContext* pContext; //デバイスコンテキスト
IDXGISwapChain* pSwapChain; //スワップチェイン
ID3D11RenderTargetView* pRenderTargetView; //レンダーターゲットビュー
//エントリーポイント
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
ウィンドウが完成した後に、上記のオブジェクトを作成していく。
専用の構造体を使い、各項目を設定していく。
//ウィンドウを表示
ShowWindow(hWnd, nCmdShow);
///////////////////////////いろいろ準備するための設定///////////////////////////////
//いろいろな設定項目をまとめた構造体
DXGI_SWAP_CHAIN_DESC scDesc;
//とりあえず全部0
ZeroMemory(&scDesc, sizeof(scDesc));
//描画先のフォーマット
scDesc.BufferDesc.Width = WINDOW_WIDTH; //画面幅
scDesc.BufferDesc.Height = WINDOW_HEIGHT; //画面高さ
scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 何色使えるか
//FPS(1/60秒に1回)
scDesc.BufferDesc.RefreshRate.Numerator = 60;
scDesc.BufferDesc.RefreshRate.Denominator = 1;
//その他
scDesc.Windowed = TRUE; //ウィンドウモードかフルスクリーンか
scDesc.OutputWindow = hWnd; //ウィンドウハンドル
scDesc.BufferCount = 1; //バックバッファの枚数
scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; //バックバッファの使い道=画面に描画するために
scDesc.SampleDesc.Count = 1; //MSAA(アンチエイリアス)の設定
scDesc.SampleDesc.Quality = 0; // 〃
続けて、上で設定した項目をもとに「デバイス」「コンテキスト」「スワップチェーン」を作成する。
////////////////上記設定をもとにデバイス、コンテキスト、スワップチェインを作成////////////////////////
D3D_FEATURE_LEVEL level;
D3D11CreateDeviceAndSwapChain(
nullptr, // どのビデオアダプタを使用するか?既定ならばnullptrで
D3D_DRIVER_TYPE_HARDWARE, // ドライバのタイプを渡す。ふつうはHARDWARE
nullptr, // 上記をD3D_DRIVER_TYPE_SOFTWAREに設定しないかぎりnullptr
0, // 何らかのフラグを指定する。(デバッグ時はD3D11_CREATE_DEVICE_DEBUG?)
nullptr, // デバイス、コンテキストのレベルを設定。nullptrにしとけばOK
0, // 上の引数でレベルを何個指定したか
D3D11_SDK_VERSION, // SDKのバージョン。必ずこの値
&scDesc, // 上でいろいろ設定した構造体
&pSwapChain, // 無事完成したSwapChainのアドレスが返ってくる
&pDevice, // 無事完成したDeviceアドレスが返ってくる
&level, // 無事完成したDevice、Contextのレベルが返ってくる
&pContext); // 無事完成したContextのアドレスが返ってくる
次に、「レンダーターゲットビュー」を作成。
///////////////////////////レンダーターゲットビュー作成///////////////////////////////
//スワップチェーンからバックバッファを取得(バックバッファ = レンダーターゲット)
ID3D11Texture2D* pBackBuffer;
pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
//レンダーターゲットビューを作成
pDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView);
//一時的にバックバッファを取得しただけなので解放
pBackBuffer->Release();
最後に「ビューポート」を設定する。
///////////////////////////ビューポート(描画範囲)設定///////////////////////////////
//レンダリング結果を表示する範囲
D3D11_VIEWPORT vp;
vp.Width = (float)WINDOW_WIDTH; //幅
vp.Height = (float)WINDOW_HEIGHT;//高さ
vp.MinDepth = 0.0f; //手前
vp.MaxDepth = 1.0f; //奥
vp.TopLeftX = 0; //左
vp.TopLeftY = 0; //上
//データを画面に描画するための一通りの設定(パイプライン)
pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // データの入力種類を指定
pContext->OMSetRenderTargets(1, &pRenderTargetView, nullptr); // 描画先を設定
pContext->RSSetViewports(1, &vp);
最低限の準備は整った。
あとは1フレーム描画するごとにデバイスコンテキストとスワップチェインに頑張ってもらえばよい。
//メッセージループ(何か起きるのを待つ)
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
{
//メッセージあり
if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//メッセージなし
else
{
//ゲームの処理
//背景の色
float clearColor[4] = { 0.0f, 0.5f, 0.5f, 1.0f };//R,G,B,A
//画面をクリア
pContext->ClearRenderTargetView(pRenderTargetView, clearColor);
//描画処理
//スワップ(バックバッファを表に表示する)
pSwapChain->Present(0, 0);
}
}
return 0;
今のところ画面に表示するものがないため、とりあえずデバイスコンテキストに画面を真っ新にしてもらい、それをディスプレイに表示さておく。
実行してみよう。
画面が暗い水色になれば成功。
作ったものは忘れないうちに開放しておく。
それぞれのクラスに「Release」というメンバ関数があるので、それを呼べばOK。
//メッセージループ(何か起きるのを待つ)
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
{
:
:
:
}
//解放処理
pRenderTargetView->Release();
pSwapChain->Release();
pContext->Release();
pDevice->Release();
return 0;
}