#はじめに
#1 ここ
#2
#3 執筆中
DirectX11の入門編ということでサンプルソースを添えて無の状態からどこまで書けるか分からないですが出来たらPBR実装とかまで書けたら良いなと思います(願望
DirectX11Managerという名のDirectX11のラッパーを制作→活用のサイクルで作っていきます
次回の内容も一部含まれていますがProjectをgithubに上げています。動く状態で置いているつもりですので
照らし合わせながら見てほしいです。
github
#初期化
DirectXとは一言で言うならばGPUを操作するためのAPI群です。
なので初期化の流れについては
- GPUをC++から扱うためのデバイスの設定
- ディスプレイのTextureを取得
の2つになります。
##Managerのヘッダー
まず最初にinclude等初期化に必要なものをまとめて紹介します。
ソース内のコメント参照
//WindowsApplicationを使用するので必須
#include <windows.h>
//DirectX11のAPIが入っている
#include <d3d11.h>
//シェーダーのコンパイラ
#include <d3dcompiler.h>
//ベクトルやFLOAT3など便利な算術ライブラリ
#include <DirectXMath.h>
//デバイスの管理をしやすくするためのDirectX版のスマートポインタのようなもの
#include <wrl/client.h>
//DirectXAPI関係のリンク
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3dcompiler.lib")
//DirectXMathがDirectXのネームスペースにある
using namespace DirectX;
//ComPtr(DirectX版のスマートポインタ)
using Microsoft::WRL::ComPtr;
//書きやすいようにtypedef色々
typedef ComPtr<ID3D11Buffer> ConstantBuffer, VertexBuffer, IndexBuffer, StructuredBuffer;
typedef ComPtr<ID3D11InputLayout> InputLayout;
typedef ComPtr<ID3D11VertexShader> VertexShader;
typedef ComPtr<ID3D11GeometryShader> GeometryShader;
typedef ComPtr<ID3D11PixelShader> PixelShader;
typedef ComPtr<ID3D11ComputeShader> ComputeShader;
typedef ComPtr<ID3D11Texture2D> Texture2D;
typedef ComPtr<ID3D11ShaderResourceView> ShaderTexture;
typedef ComPtr<ID3D11UnorderedAccessView> ComputeOutputView;
//管理クラス
class DirectX11Manager
{
//Windowsのハンドル
HWND hWnd = NULL;
public:
//DX11のデバイス
ComPtr<ID3D11Device> m_pDevice = nullptr;
//DX11の描画命令等を送るためのもの
ComPtr<ID3D11DeviceContext> m_pImContext = nullptr;
//ハードウェアの情報が詰まっているもの
ComPtr<IDXGISwapChain> m_pSwapChain = nullptr;
//ディスプレイのバッグバッファのテクスチャ
Texture2D m_pRTTex = nullptr;
//ディスプレイのバッグバッファのテクスチャを描画先として指定できるようにしたもの
ComPtr<ID3D11RenderTargetView> m_pRTView = nullptr;
//ウィンドウのサイズの指定
D3D11_VIEWPORT m_Viewport = { 0,0,0,0,0,0 };
//初期化関数
HRESULT Init(HINSTANCE hInstance, int cCmdShow);
};
##Windows関係の初期化
次にInit関数の実装について
HRESULT DirectX11Manager::Init(HINSTANCE hInstance, int cCmdShow)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, hInstance, NULL, NULL, NULL, NULL, "DirectX11 Template", NULL };
if (!RegisterClassEx(&wcex)) {
return E_FAIL;
}
RECT rc = { 0, 0, 1270, 760 };
hWnd = CreateWindow(wcex.lpszClassName, "DeferredRenderer", WS_DLGFRAME,
CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top,
NULL, NULL, hInstance, NULL);
if (!hWnd) {
return E_FAIL;
}
ShowWindow(hWnd, cCmdShow);
UpdateWindow(hWnd);
まず最初にWinAPIを使用してWindowの作成を行います。CreateWindow系の説明は省略。
##ハードウェアのチェック
#pragma region HardWare Check
IDXGIFactory* factory;
IDXGIAdapter* adapter;
IDXGIOutput* adapterOutput;
unsigned int numModes = 0;
size_t stringLength;
DXGI_ADAPTER_DESC adapterDesc;
//グラフィック インタフェース ファクトリを作成
auto hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)& factory);
if (FAILED(hr))
{
return hr;
}
int GPUNumber = 0;
int GPUMaxMem = 0;
//一番強いGPUアダプタを検索
for (int i = 0; i < 100; i++)
{
IDXGIAdapter* add;
hr = factory->EnumAdapters(i, &add);
if (FAILED(hr))
break;
hr = add->GetDesc(&adapterDesc);
char videoCardDescription[128];
//ビデオカード名を取得
int error = wcstombs_s(&stringLength, videoCardDescription, 128, adapterDesc.Description, 128);
if (error != 0)
{
break;
}
cout << "ビデオカード名 : " << videoCardDescription << endl;
//ビデオカードメモリを取得(MB単位)
int videoCardMemory = (int)(adapterDesc.DedicatedVideoMemory / 1024 / 1024);
cout << "ビデオメモリー : " << videoCardMemory << endl;
//アウトプット(モニター)に番号IDを付ける
hr = add->EnumOutputs(0, &adapterOutput);
if (FAILED(hr))
{
continue;
}
//DXGI_FORMAT_R8G8B8A8_UNORM の表示形式数を取得する
hr = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
if (FAILED(hr))
{
continue;
}
cout << "RBGA8_UNORM Count : " << numModes << endl;
if (videoCardMemory > GPUMaxMem)
{
GPUMaxMem = videoCardMemory;
GPUNumber = i;
}
add->Release();
//アダプタアウトプットを解放
adapterOutput->Release();
adapterOutput = 0;
}
//グラフィック インタフェース アダプターを作成
hr = factory->EnumAdapters(GPUNumber, &adapter);
if (FAILED(hr))
{
return hr;
}
#pragma endregion
次にやっているのはハードウェアのチェックです。
環境によってはGPUが複数あって0番目のGPUが一番使ってほしいものとは限らないと思うので
プログラム側で勝手にGPUメモリの一番大きいものを選ぼうとしています。
factoryからアダプターを取得して言って一番メモリ量が多いアダプターを設定しているだけです。
##DirectXの初期化
#pragma region DirectX11Init
UINT cdev_flags = 0;
#ifdef _DEBUG
cdev_flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
// スワップチェイン設定
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 1;
sd.BufferDesc.Width = rc.right;
sd.BufferDesc.Height = rc.bottom;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1; //1/60 = 60fps
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = true;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
};
// DirectX11デバイスとスワップチェイン作成
hr = D3D11CreateDeviceAndSwapChain(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL,
cdev_flags, featureLevels, 1, D3D11_SDK_VERSION, &sd,
&m_pSwapChain, &m_pDevice, NULL, &m_pImContext);
if (FAILED(hr)) {
return hr;
}
//アダプタを解放
adapter->Release();
adapter = 0;
//ファクトリを解放
factory->Release();
factory = 0;
// スワップチェインに用意されたバッファ(2Dテクスチャ)を取得
hr = m_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&m_pRTTex));
if (FAILED(hr)) {
return hr;
}
// レンダーターゲットView作成
hr = m_pDevice->CreateRenderTargetView(m_pRTTex.Get(), NULL, &m_pRTView);
if (FAILED(hr)) {
return hr;
}
// viewport
m_Viewport.Width = static_cast<FLOAT>(rc.right - rc.left);
m_Viewport.Height = static_cast<FLOAT>(rc.bottom - rc.top);
m_Viewport.MinDepth = 0.0f;
m_Viewport.MaxDepth = 1.0f;
m_Viewport.TopLeftX = 0;
m_Viewport.TopLeftY = 0;
#pragma endregion
return hr;
}
スワップチェインを設定をしてD3D11CreateDeviceAndSwapChainを利用してDevice,Comtext,Swapchainを作成しています。
スワップチェインに用意されたテクスチャはディスプレイのバッグバッファにあるテクスチャになるのでそのテクスチャを取得します。
その後そのテクスチャを使って描画先に設定できる形(RenderTarget)を作成しています。
ViewPortはそのままWindowの実際に描画する範囲です。
#表示
上の初期化処理を書けば一応DirectXの初期化が出来たことになります
このようなcppを書くと無のWindowをDirectXで初期化して表示出来たことになるでしょう。
#include "DirectX11Manager.h"
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
DirectX11Manager manager;
if (FAILED(manager.Init(hInstance, nCmdShow)))
return -1;
MSG msg = { 0 };
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (WM_QUIT == msg.message) return 0;
}
return 0;
}
#ポリゴンの表示
このままではDirectXでやっている感じは何もしないですね。
ポリゴンの表示が出来るようになって初めて初期化終了と言えるでしょう。
ポリゴンが表示されるまでに何が起こっているのかをまず整理してみましょう。
- CPUで頂点情報を作成
- GPUで扱えるようにGPUに頂点情報をコピー
- 頂点情報をGPUで処理
- 処理された頂点情報からラスタライズ
- ラスタライズされたピクセルに対して色を塗る
という流れになっています。
これをするために何をしないといけないかを次に整理してみます。
- 頂点シェーダー&インプットレイアウトの作成
- ピクセルシェーダーの作成
- 頂点バッファの作成
- インデックスバッファの作成
- 1ー4で作成したデータをセット
- DrawCall!!!
大体三角形一つ作るのにこれぐらいの手順が必要です。概要だけでもなかなかにボリューム満点ですね。
##頂点シェーダー&ピクセルシェーダーの作成(HLSL)
頂点シェーダーとピクセルシェーダーについて軽く解説すると
頂点シェーダーとはCPUから受け取った頂点をどのように処理するか書きます。
ピクセルシェーダーとは頂点シェーダーから受け取ったデータを処理をして色をつけます
描画するには頂点データが必須です。
頂点データには自由にデータを割り振る事ができます。
今回は座標と色情報を頂点データに含めたいと思います。
それに対応した頂点シェーダを作ります。
struct VS_INPUT
{
float3 Pos : POSITION;
float4 Col : TEXCOORD;
};
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float4 Col : TEXCOORD;
};
PS_INPUT vsMain(VS_INPUT pos)
{
PS_INPUT o = (PS_INPUT)0;
o.Pos = float4(pos.Pos, 1);
o.Col = pos.Col;
return o;
}
float4 psMain(PS_INPUT input) : SV_TARGET
{
return input.Col;
}
単純にやってきた頂点データをそのままの形で流して居ます。
色についても頂点データについている色をそのまま流しています。
##頂点シェーダーとピクセルシェーダーとインプットレイアウトの作成(C++)
まずラッパー関数を追加します
ID3D11VertexShader* CreateVertexShader(const string& filename, const string& entrypath = "", bool erorr = true);
ID3D11PixelShader* CreatePixelShader(const string& filename, const string& entrypath = "", bool erorr = true);
//inputlayout作成
ID3D11InputLayout* CreateInputLayout(D3D11_INPUT_ELEMENT_DESC* layout, UINT elem_num, const string& filename, const string& entrypath = "");
ID3D11VertexShader* DirectX11Manager::CreateVertexShader(const string & filename, const string & entrypath, bool erorr)
{
ID3D11VertexShader* Shader;
#if defined(_DEBUG)
// グラフィックデバッグツールによるシェーダーのデバッグを有効にする
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif
ComPtr<ID3DBlob> blob;
wchar_t ws[512];
setlocale(LC_CTYPE, "jpn");
mbstowcs(ws, filename.c_str(), 512);
ComPtr<ID3DBlob> pErrorBlob = NULL;
HRESULT hr = D3DCompileFromFile(ws, nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entrypath.c_str(), "vs_5_0", compileFlags, 0, &blob, &pErrorBlob);
// エラーチェック.
if (FAILED(hr))
{
if (erorr)
{
// エラーメッセージを出力.
if (pErrorBlob != NULL)
{
MessageBox(NULL, (char*)pErrorBlob->GetBufferPointer(), "", 0);
return nullptr;
}
}
else
{
string er = (char*)pErrorBlob->GetBufferPointer();
if (er.find("entrypoint not found") == string::npos)
MessageBox(NULL, (char*)pErrorBlob->GetBufferPointer(), "", 0);;
cout << filename << "(" << entrypath << ") is notfound" << endl;
return nullptr;
}
}
hr = m_pDevice->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), NULL, &Shader);
assert(SUCCEEDED(hr));
return Shader;
}
ID3D11PixelShader* DirectX11Manager::CreatePixelShader(const string & filename, const string & entrypath, bool erorr)
{
ID3D11PixelShader* Shader;
#if defined(_DEBUG)
// グラフィックデバッグツールによるシェーダーのデバッグを有効にする
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif
ComPtr<ID3DBlob> blob;
wchar_t ws[512];
setlocale(LC_CTYPE, "jpn");
mbstowcs(ws, filename.c_str(), 512);
ComPtr<ID3DBlob> pErrorBlob = NULL;
HRESULT hr = D3DCompileFromFile(ws, nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entrypath.c_str(), "ps_5_0", compileFlags, 0, &blob, &pErrorBlob);
// エラーチェック.
if (FAILED(hr))
{
if (erorr) {
// エラーメッセージを出力.
if (pErrorBlob != NULL && erorr)
{
MessageBox(NULL, (char*)pErrorBlob->GetBufferPointer(), "", 0);
return nullptr;
}
}
else
{
string er = (char*)pErrorBlob->GetBufferPointer();
if (er.find("entrypoint not found") == string::npos)
MessageBox(NULL, (char*)pErrorBlob->GetBufferPointer(), "", 0);;
cout << filename << "(" << entrypath << ") is notfound" << endl;
return nullptr;
}
}
hr = m_pDevice->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), NULL, &Shader);
assert(SUCCEEDED(hr));
return Shader;
}
ID3D11InputLayout* DirectX11Manager::CreateInputLayout(D3D11_INPUT_ELEMENT_DESC * layout, UINT elem_num, const string & filename, const string & entrypath)
{
ID3D11InputLayout* pVertexLayout;
#if defined(_DEBUG)
// グラフィックデバッグツールによるシェーダーのデバッグを有効にする
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif
ComPtr<ID3DBlob> blob;
wchar_t ws[512];
setlocale(LC_CTYPE, "jpn");
mbstowcs(ws, filename.c_str(), 512);
ComPtr<ID3DBlob> pErrorBlob = NULL;
HRESULT hr = D3DCompileFromFile(ws, nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entrypath.c_str(), "vs_5_0", compileFlags, 0, &blob, &pErrorBlob);
// エラーチェック.
if (FAILED(hr))
{
// エラーメッセージを出力.
if (pErrorBlob != NULL)
{
MessageBox(NULL, (char*)pErrorBlob->GetBufferPointer(), "", 0);
}
}
hr = m_pDevice->CreateInputLayout(layout, elem_num, blob->GetBufferPointer(),
blob->GetBufferSize(), &pVertexLayout);
assert(SUCCEEDED(hr));
return pVertexLayout;
}
シェーダーをコンパイルとインプットレイアウトを作成している関数です。
これを使ってMain関数に追記します。
//Shaderを作成
VertexShader vs;
PixelShader ps;
InputLayout il;
vs.Attach(manager.CreateVertexShader("Assets/Shaders/2DPipeLine.hlsl", "vsMain"));
ps.Attach(manager.CreatePixelShader("Assets/Shaders/2DPipeLine.hlsl", "psMain"));
//InputLayoutの作成
D3D11_INPUT_ELEMENT_DESC elem[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
};
il.Attach(manager.CreateInputLayout(elem, 2, "Assets/Shaders/2DPipeLine.hlsl", "vsMain"));
関数を呼んで作成しているだけです。
インプットレイアウトの設定項目について解説します。
今回は座標と色情報を頂点データに含めます。
触れていませんでしたがシェーダーのstruct VS_INPUTの中に入っている変数の後ろに:POSITIONというものがあると思います。
それをセマンティクスと言って名前でデータを判断するために使われています。
VS_INPUTのセマンティクスとD3D11_INPUT_ELEMENT_DESCの第一引数を同じ名前にしないといけません。
第3引数はその形のサイズを指定しますPOSITIONはxyzの値なのでRGBの3つ。
TEXCOORD(色)はRGBAなのでRGBAの4つ等型にあったフォーマットを指定します。
第5引数はそのデータの格納されているバイト数を記述します。
TEXCOORDに12と書いてあるのはPOSITIONのバイト数がfloat(4)*xyz(3)の12になっています。
頂点情報が増えるたびにそこまでのすべての情報の加算した値になれば大丈夫です。
##頂点バッファ、インデックスバッファの作成
例の如くラッパー関数を追加します
template<class x>
ID3D11Buffer* CreateVertexBuffer(x* VertexData, UINT VertexNum)
{
//頂点バッファ作成
D3D11_BUFFER_DESC hBufferDesc;
ZeroMemory(&hBufferDesc, sizeof(hBufferDesc));
hBufferDesc.ByteWidth = sizeof(x) * VertexNum;
hBufferDesc.Usage = D3D11_USAGE_DEFAULT;
hBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
hBufferDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA hSubResourceData;
ZeroMemory(&hSubResourceData, sizeof(hSubResourceData));
hSubResourceData.pSysMem = VertexData;
ID3D11Buffer* hpBuffer;
if (FAILED(m_pDevice->CreateBuffer(&hBufferDesc, &hSubResourceData, &hpBuffer))) {
return nullptr;
}
return hpBuffer;
}
ID3D11Buffer* CreateIndexBuffer(UINT* Index, UINT IndexNum)
{
//インデックスバッファ作成
D3D11_BUFFER_DESC hBufferDesc;
ZeroMemory(&hBufferDesc, sizeof(hBufferDesc));
hBufferDesc.ByteWidth = sizeof(UINT) * IndexNum;
hBufferDesc.Usage = D3D11_USAGE_DEFAULT;
hBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
hBufferDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA hSubResourceData;
ZeroMemory(&hSubResourceData, sizeof(hSubResourceData));
hSubResourceData.pSysMem = Index;
ID3D11Buffer* hpBuffer;
if (FAILED(m_pDevice->CreateBuffer(&hBufferDesc, &hSubResourceData, &hpBuffer))) {
return nullptr;
}
return hpBuffer;
}
バッファを作成している関数です。
これを使ってMain関数に追記します。
//頂点情報を設定
struct Vertex
{
XMFLOAT3 pos;
XMFLOAT4 col;
};
vector<Vertex> vertexs =
{
{ XMFLOAT3(-0.5f,-0.5f,0), XMFLOAT4(1,0,0,1)},
{ XMFLOAT3(0.5f,-0.5f,0), XMFLOAT4(0,1,0,1)},
{ XMFLOAT3(0.5f, 0.5f,0), XMFLOAT4(0,0,1,1)},
{ XMFLOAT3(-0.5f, 0.5f,0), XMFLOAT4(0,0,0,1)}
};
VertexBuffer vb;
vb.Attach(manager.CreateVertexBuffer(vertexs.data(), static_cast<UINT>(vertexs.size())));
//インデックス情報の設定
vector<UINT> idxs = { 0,1,2,0,2,3 };
IndexBuffer ib;
ib.Attach(manager.CreateIndexBuffer(idxs.data(), static_cast<UINT>(idxs.size())));
関数を呼んで作成しているだけです。
posの値はディスプレイの中心が(0,0)左上が(-1,1)右下が(1,-1)というような-1~1の範囲内で指定をします。
##情報のセット、DrawCall!!
例の如くラッパー関数を追加します!!
//PipelineSetting
void SetInputLayout(ID3D11InputLayout* VertexLayout);
void SetVertexShader(ID3D11VertexShader* vs);
void SetPixelShader(ID3D11PixelShader* ps);
void SetVertexBuffer(ID3D11Buffer* VertexBuffer, UINT VertexSize);
void SetIndexBuffer(ID3D11Buffer* IndexBuffer);
void DrawBegin();
void DrawEnd();
void DrawIndexed(UINT VertexNum);
void DirectX11Manager::SetInputLayout(ID3D11InputLayout* VertexLayout)
{
m_pImContext->IASetInputLayout(VertexLayout);
}
void DirectX11Manager::SetVertexShader(ID3D11VertexShader* vs)
{
m_pImContext->VSSetShader(vs, nullptr, 0);
}
void DirectX11Manager::SetPixelShader(ID3D11PixelShader* ps)
{
m_pImContext->PSSetShader(ps, nullptr, 0);
}
void DirectX11Manager::SetVertexBuffer(ID3D11Buffer* VertexBuffer, UINT VertexSize)
{
UINT hOffsets = 0;
m_pImContext->IASetVertexBuffers(0, 1, &VertexBuffer, &VertexSize, &hOffsets);
}
void DirectX11Manager::SetIndexBuffer(ID3D11Buffer* IndexBuffer)
{
m_pImContext->IASetIndexBuffer(IndexBuffer, DXGI_FORMAT_R32_UINT, 0);
}
void DirectX11Manager::SetTexture2D(UINT RegisterNo, ID3D11ShaderResourceView* Texture)
{
m_pImContext->PSSetShaderResources(RegisterNo, 1, &Texture);
}
void DirectX11Manager::DrawBegin()
{
//ポリゴンの生成方法の指定
m_pImContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 指定色で画面クリア
float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; //red,green,blue,alpha
m_pImContext->ClearRenderTargetView(m_pRTView.Get(), ClearColor);
m_pImContext->RSSetViewports(1, &m_Viewport);
//RenderTargetをバックバッファ
ID3D11RenderTargetView* rtv[1] = { m_pRTView.Get() };
m_pImContext->OMSetRenderTargets(1, rtv, nullptr);
}
void DirectX11Manager::DrawEnd()
{
m_pSwapChain->Present(0, 0);
}
void DirectX11Manager::DrawIndexed(UINT VertexNum)
{
m_pImContext->DrawIndexed(VertexNum, 0, 0);
}
先程作ったシェーダーやバッファを設定するためのラッパー関数と描画のはじめと終わりの関数を作っています。
DrawBegin関数にはポリゴンの生成方法を指定、バックバッファのテクスチャをクリア、ViewPortの設定、バックバッファをレンダーターゲットとして設定をしています。
DrawEnd関数でバックバッファに書き込んだものをディスプレイ側に切り替えをしています。これを呼んで初めて書き込んだものが見えるところに出てきます。
これを使ってMain関数に追記します。
//MainLoop
manager.DrawBegin();
//ポリゴンを書くための各種パラメータセット
manager.SetVertexShader(vs.Get());
manager.SetPixelShader(ps.Get());
manager.SetInputLayout(il.Get());
manager.SetVertexBuffer(vb.Get(), sizeof(Vertex));
manager.SetIndexBuffer(ib.Get());
//DrawCall
manager.DrawIndexed(static_cast<UINT>(idxs.size()));
manager.DrawEnd();
関数を呼んで作成しているだけです。
ここまでやって初めてポリゴン(長方形)を表示することが出来ます
結果を見るとわかるのですが頂点の色情報をどのように設定したでしょうか?
頂点には各単色を指定しているはずなのにグラデーションしています。
頂点と頂点の間のデータについては内部で勝手に線形補間されています。
これによって簡単にグラデーションを作ることが出来ます。
なのでデータとしては頂点シェーダーからピクセルシェーダーに流れているのですが頂点シェーダーを抜けたときの値がそのままピクセルシェーダーで使われるのではなくて頂点のデータ同士を補完したものに対してピクセルシェーダーは処理をします。補完したくないデータでも補完してしまいます。
#まとめ
DirectX11の初期化からポリゴンの表示までやってみました。正直個人的に初学者の方に分かって欲しい事については詳しく書いてますが何も説明の無い部分もあります。最初からすべてを分かっている必要は無いと思っているのでそのあたりは必要にかられた時に随時説明されると思います。
githubにあげているプロジェクトにはテクスチャの表示まで入っています。これは次回の内容を一部含んでいることになります。