対象
プログラムをある程度理解し、Visual Studio 2019の使い方をある程度理解している人が対象です。
理由としては、説明がそこらへん理解している前提で端折ってるからです。
目的
自己満。(怒られたら消します。)(内容について反論ありましたら先に言わせていただきます。勉強不足で申し訳ありません。精進します。)
構成
No.1 DirectX SDKのダウンロード
No.2 プロジェクトの作成からDirectX SDKの設定
No.3 必要プロジェクトファイルの作成と設定
No.4 最低限コード記述と各々の説明
本編
文字のみの説明で行くぜ!(大変気分の良いときに書きました。)
No.1 DirectX SDKのダウンロード
ブラウザにて[DirectX SDK]と検索して[Microsoft]のサイトからダウンロードしてください。
No.2 プロジェクトの作成からDirectX SDKの設定
プロジェクトの作成と補足
Visual Studio 2019にてプロジェクトを作成してください。
作成しましたらこれにてDirectX11とDirectX12の環境設定は終了です。本当です。
ですが、わざわざDirectX SDKを入れさせました。
この理由は単純で、あなた方が購読するであろう参考書類はDirectX SDKを使う前提で行っているからです。
参考書類に合わせてDirectX SDKを入れる理由も単純で、DirectXで行列計算などを取り扱うメソッドがDirectX SDKからWindows Kitsへ移行する際に変わりました。
まぁ、その点で言えばDirectXは全部のバージョンでウィンドウ作成のための下準備のための構造体は作り直されています。
そういう意味では、DirectX12が一番作りやすくなってます。そこら辺について少しだけでも見たい人は以下のURLで見てみてください。
DirectX3D 11とDirectX3D 12の間の重要な変更点
ちなみに、今回はDirectX 12を説明しませんが以下のURL先から簡単なものが作成できます。
私はDirectX 12の参考書籍などをまだ購読していないのでわかりませんが、もしかしたらそちらの方ではDirectX SDKは不要になっているかもしれません。
Direct3D 12 の基本的なコンポーネントの作成
DirectX SDKの設定
全ての構成の状態で、C/C++の全般にて、追加のインクルードディレクトリにDirectX SDKのincludeを設定してください。
同様に、全ての構成の状態で、リンカーの全般にて、追加のライブラリディレクトリにDirectX SDKのLibの適応プラットフォームを設定してください。
No.3 必要プロジェクトファイルの作成と設定
main.cppをいつものように作成してください。
次に新しい項目の追加からHLSLを選択してピクセルシェーダと頂点シェーダを追加してください。ファイル名はややこしくなるのでそのままにしといてください。
それでファイルの作成は終了ですが、頂点シェーダとピクセルシェーダはファイルのコンパイル設定を変更します。
これをやらないやり方(main.cppの一部を変更)もありますが、覚えてもらいたいので行います。
リソースファイルのPixelShader.hlslとVertexShader.hlslを右クリックしてプロパティを選択します。
そうしたらHLSLコンパイラの全般をクリックしてシェーダモデルをShader Model 5.0(/5_0)にしてOKを押して閉じてください。
No.4 最低限コード記述と各々の説明
頂点シェーダとピクセルシェーダの中にはすでにコードが書かれていると思いますがそのままで構いません。
それが一番シンプルなものです。
ではmain.cppを記述します。
補足ですが、コードを書く以上命名規則など私の基準があるので一応説明しときます。
私は、メンバ変数に対しては、m_nameのようにします。
また、メンバ変数には、小文字から始まり途中で必要になり次第大文字を使います。(m_playerName)
メンバ変数に限らず、関数に対しては大文字開始です。(Draw())
関数の引数に対しては、t_nameのようにします。
ローカル変数に対しては、メンバ変数のm_が無いただの変数名です。
STLの変数に対しては、例えばvectorであればv_mapのように頭文字を使うようにしています。
ポインタ変数に関しては、p_hpのようにします。
列挙型クラスに関しては、EMemberのようにします。
構造体に関しては、SMemberのようにします。
おおよそこんな感じです。
まぁ以下のコードはあるページを後程添付するURLを参考にしているので100%私のコードではないです。
まず、DirectX11のためのインクルードを行います。
#pragma comment(lib,"d3d11.lib") #pragma comment(lib,"d3dx11.lib") #pragma comment(lib,"d3dCompiler.lib") #include#include
次にマクロと定数を設定します。基本的に開放を行ったら中身を初期化させたいのでマクロを使います。
// 安全に解放する #define SAFE_RELEASE(x) if(x){x->Release(); x=nullptr;} /* ちなみにマクロはこの書き方でも行けます。 #define SAFE_RELEASE(x) \ { \ x->Release(); \ x = nullptr; \ } */ // 定数定義 const int WINDOW_WIDTH = 320; // ウィンドウ幅 const int WINDOW_HEIGHT = 240; // ウィンドウ高さ
今回はクラスを使わない形で行うため基本的に主要な構造体変数をグローバル変数として扱います。
では、その変数を定義します。
// グローバル変数 HWND m_hWnd = nullptr; ID3D11Device* m_device = nullptr; // デバイス ID3D11DeviceContext* m_deviceContext = nullptr; // デバイスコンテキスト IDXGISwapChain* m_swapChain = nullptr; // スワップチェイン ID3D11RenderTargetView* m_renderTargetView = nullptr; // レンダーターゲットビュー ID3D11InputLayout* m_vertexLayout = nullptr; ID3D11Buffer* m_vertexBuffer = nullptr; ID3D11VertexShader* m_vertexShader = nullptr; // 頂点シェーダー ID3D11PixelShader* m_pixelShader = nullptr; // ピクセルシェーダー
ポリゴンの頂点を設定するためのクラスと構造体を定義します。
DxLibでは既にVECTORがあります。
// ベクタークラス class VECTOR3 { private: float m_x; float m_y; float m_z; public: VECTOR3(float t_x, float t_y, float t_z) : m_x(t_x), m_y(t_y), m_z(t_z) {} }; // 頂点の構造体 struct SSimpleVertex { VECTOR3 pos; // 位置 };
Direct3Dの初期化を行う関数を定義します。次からこの中について記述します。
// Direct3Dの初期化関数 HRESULT InitD3D(HWND hWnd) { }
まず、デバイスとスワップチェーン(スワップチェイン)を作成します。
これらについて簡単に言えば、デバイスはどういったウィンドウにするかの設定で、スワップチェーンは裏画面とか表画面の話です。
// デバイスとスワップチェーンの作成 DXGI_SWAP_CHAIN_DESC sd; ZeroMemory(&sd, sizeof(sd)); sd.BufferCount = 1; // バックバッファの数 sd.BufferDesc.Width = WINDOW_WIDTH; // バッファの幅 sd.BufferDesc.Height = WINDOW_HEIGHT; // バッファの高さ sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // バッファのフォーマット sd.BufferDesc.RefreshRate.Numerator = 60; // リフレッシュレート sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.OutputWindow = hWnd; sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.Windowed = TRUE; D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_REFERENCE, NULL, 0, &featureLevel, 1, D3D11_SDK_VERSION, &sd, &m_swapChain, &m_device, NULL, &m_deviceContext))) { return FALSE; }
次にレンダーターゲットを作成します。今回はシングルレンダーターゲットです。
簡単に言えば、裏画面から表画面へ直行しないで中間レンダーを作成するために設定するためのものです。
それにより出来ることは、例えばブレンド処理とかマスク処理とか、画像に一工夫入れてから描画するもの。
// レンダーターゲットビューの作成 ID3D11Texture2D* backBuffer; m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)& backBuffer); m_device->CreateRenderTargetView(backBuffer, NULL, &m_renderTargetView); backBuffer->Release(); m_deviceContext->OMSetRenderTargets(1, &m_renderTargetView, NULL);
次にビューポートを設定します。
簡単に言えば、3D上でカメラがあると思いますが、そのカメラに写っているものをどう扱うかといったものです。
// ビューポートの設定 D3D11_VIEWPORT vp; vp.Width = WINDOW_WIDTH; vp.Height = WINDOW_HEIGHT; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; m_deviceContext->RSSetViewports(1, &vp);
次にシェーダを使うためのコンパイルやエラー処理をする親的存在であるブロブを作成する。
// hlslファイル読み込み ID3DBlob* p_CompiledShader = nullptr; ID3DBlob* p_Errors = nullptr;
次に頂点シェーダの構築と処理を行う。
頂点シェーダは簡単に言えば三角ポリゴンの各頂点をどう処理するかといったもの。
今回はただ頂点情報を受け取って何の処理もせず渡すだけの役割をしている。
// ブロブから頂点シェーダー作成 if (FAILED(D3DX11CompileFromFile(L"VertexShader.hlsl", NULL, NULL, "main", "vs_5_0", 0, 0, NULL, &p_CompiledShader, &p_Errors, NULL))) { MessageBox(0, L"頂点シェーダー読み込み失敗", NULL, MB_OK); return E_FAIL; } SAFE_RELEASE(p_Errors); if (FAILED(m_device->CreateVertexShader(p_CompiledShader->GetBufferPointer(), p_CompiledShader->GetBufferSize(), NULL, &m_vertexShader))) { SAFE_RELEASE(p_CompiledShader); MessageBox(0, L"頂点シェーダー作成失敗", NULL, MB_OK); return E_FAIL; } // 頂点インプットレイアウトを定義 D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; UINT numElements = sizeof(layout) / sizeof(layout[0]); // 頂点インプットレイアウトを作成 if (FAILED(m_device->CreateInputLayout(layout, numElements, p_CompiledShader->GetBufferPointer(), p_CompiledShader->GetBufferSize(), &m_vertexLayout))) return FALSE; // 頂点インプットレイアウトをセット m_deviceContext->IASetInputLayout(m_vertexLayout);
次にピクセルシェーダの構築と処理を行う。
ピクセルシェーダは簡単に言えば頂点シェーダから渡された情報にそれの色情報を付与するもの。
// ブロブからピクセルシェーダー作成 if (FAILED(D3DX11CompileFromFile(L"PixelShader.hlsl", NULL, NULL, "main", "ps_5_0", 0, 0, NULL, &p_CompiledShader, &p_Errors, NULL))) { MessageBox(0, L"ピクセルシェーダー読み込み失敗", NULL, MB_OK); return E_FAIL; } SAFE_RELEASE(p_Errors); if (FAILED(m_device->CreatePixelShader(p_CompiledShader->GetBufferPointer(), p_CompiledShader->GetBufferSize(), NULL, &m_pixelShader))) { SAFE_RELEASE(p_CompiledShader); MessageBox(0, L"ピクセルシェーダー作成失敗", NULL, MB_OK); return E_FAIL; } SAFE_RELEASE(p_CompiledShader);
描画する頂点の位置を設定する。
3D空間上なのでZ字句を固定で中心からそれぞれ三点に頂点を設定している。
// 三角形 SSimpleVertex vertices[] = { VECTOR3(0.0f, 0.5f, 0.5f), VECTOR3(0.5f, -0.5f, 0.5f), VECTOR3(-0.5f, -0.5f, 0.5f), }; D3D11_BUFFER_DESC bd; bd.Usage = D3D11_USAGE_DEFAULT; bd.ByteWidth = sizeof(SSimpleVertex) * 3; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = 0; bd.MiscFlags = 0; D3D11_SUBRESOURCE_DATA InitData; InitData.pSysMem = vertices; if (FAILED(m_device->CreateBuffer(&bd, &InitData, &m_vertexBuffer))) return FALSE;
次にバーテックスバッファーに三つの頂点情報を渡す。
// バーテックスバッファーをセット UINT stride = sizeof(SSimpleVertex); UINT offset = 0; m_deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
次に三角形の三つの頂点をどのようにGPUに渡すかプリミティブ・トポロジーにて設定します。
ちなみに今の設定は単純で簡単ですがとても非効率です。
// プリミティブ・トポロジーをセット m_deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ここまでエラー無いので初期化完了~
return S_OK;
描画を行う関数の中身を記述。
// レンダリング void Render() { }
背景色を設定する。
float ClearColor[4] = { 0,0,0,1 }; // 消去色 m_deviceContext->ClearRenderTargetView(m_renderTargetView, ClearColor);// 画面クリア
次に使用する頂点シェーダとピクセルシェーダを設定する。
この順番は逆にしちゃダメだし一緒に扱わないとダメ。
// 使用するシェーダーの登録 m_deviceContext->VSSetShader(m_vertexShader, NULL, 0); m_deviceContext->PSSetShader(m_pixelShader, NULL, 0);
次に頂点情報をGPUに渡して描画する
// プリミティブをレンダリング m_deviceContext->Draw(3, 0); m_swapChain->Present(0, 0);// フリップ
次に裏画面を表画面にして終わり。
m_swapChain->Present(0, 0);// フリップ
構造体変数を軒並み解放
// 終了時解放処理 void Cleanup() { SAFE_RELEASE(m_vertexShader); SAFE_RELEASE(m_pixelShader); SAFE_RELEASE(m_vertexBuffer); SAFE_RELEASE(m_vertexLayout); SAFE_RELEASE(m_swapChain); SAFE_RELEASE(m_renderTargetView); SAFE_RELEASE(m_deviceContext); SAFE_RELEASE(m_device); }
エラー時のメッセージウィンドウを出力するように関数作成。
コールバック関数ともいう。
// メッセージプロシージャ LRESULT CALLBACK MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY://終了時 Cleanup(); PostQuitMessage(0); break; } return DefWindowProc(hWnd, msg, wParam, lParam); }
い つ も の
//メイン関数 INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szStr, INT iCmdShow) { }
どういったウィンドウにするかの基本的な部分の設定を行う。
// ウインドウクラスの登録 WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"Window1", NULL }; RegisterClassEx(&wc);
ウィンドウの外郭に関することの設定を行う。
// タイトルバーとウインドウ枠の分を含めてウインドウサイズを設定 RECT rect; SetRect(&rect, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); rect.right = rect.right - rect.left; rect.bottom = rect.bottom - rect.top; rect.top = 0; rect.left = 0;
ウィンドウの生成の場所や名前を設定して作成。
// ウインドウの生成 m_hWnd = CreateWindow(L"Window1", L"三角ポリゴン", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rect.right, rect.bottom, NULL, NULL, wc.hInstance, NULL);
エラーメッセージの初期化をし、Direct3Dの初期化をし、ウィンドウ表示を行い、
エラーが起きればエラーメッセージを表示させるようにさせ、エラーが無ければレンダリングをさせる。
MSG msg; ZeroMemory(&msg, sizeof(msg)); // Direct3D初期化 if (SUCCEEDED(InitD3D(m_hWnd))) { // ウインドウ表示 ShowWindow(m_hWnd, SW_SHOW); UpdateWindow(m_hWnd); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { Render(); } } }
はい
// 終了 return 0;
※参考サイト
三角ポリゴンを表示する