シェーダ管理クラス
では続いてバイナリコードから頂点シェーダとピクセルシェーダを生成・管理する実装を見ていきましょう。
今回はシェーダを管理する規定クラスを用意して作っていきます。
テンプレートクラスを使用するのでテンプレートの知識が必要になります。
Shader.h
// シェーダ管理用クラステンプレート
template<class T>
class Shader
{
public:
Shader(void) : m_pShader(NULL) {}
virtual ~Shader(void) { Finalize(); }
// 初期化
virtual bool Initialize(DirectX11& directX, const char* pFilename, const char* pEntryPoint) = 0;
// 終了処理
virtual void Finalize(void) { SafeRelease(m_pShader); }
// シェーダの取得
T* GetShader(void) { return m_pShader; }
protected:
// コンパイル
bool Compile(const char* pFilename, const char* pEntryPoint, const char* pShaderModel, ID3DBlob** ppBlob);
protected:
T* m_pShader; // シェーダ
};
// VertexShaderクラス
class VertexShader : public Shader<ID3D11VertexShader>
{
public:
VertexShader(void) : m_pShaderCode(NULL) {}
// 初期化
bool Initialize(DirectX11& directX, const char* pFilename, const char* pEntryPoint) override;
// 終了処理
void Finalize(void) override;
// シェーダコードの取得
ID3DBlob* GetShaderCode(void) { return m_pShaderCode; }
private:
ID3DBlob* m_pShaderCode;
};
// PixelShaderクラス
class PixelShader : public Shader<ID3D11PixelShader >
{
public:
// 初期化
bool Initialize(DirectX11& directX, const char* pFilename, const char* pEntryPoint) override;
};
今回はShaderという名前でクラステンプレートを用意しています。
これを継承することで重複するコードを削減しています。
機能は単純です。
- 初期化
- 解放
- シェーダの取得
- コンパイル
これだけです。
継承した先ではそれぞれコンパイルしたバイナリコードからシェーダを生成します。
Shader.cpp
include "Shader.h"
// シェーダコンパイル周りの機能をインクルード
#include <d3dcompiler.h>
// シェーダコンパイル用の静的ライブラリをリンク
#pragma comment(lib, "d3dcompiler.lib")
// コンパイル
template<class T>
bool Shader<T>::Compile(const char* pFilename, const char* pEntryPoint, const char* pShaderModel, ID3DBlob** ppBlob)
{
WCHAR path[256];
size_t len = 0;
mbstowcs_s(&len, path, 256, pFilename, _TRUNCATE);
ID3DBlob* pErrorMsg;
HRESULT hr = D3DCompileFromFile(
path,
NULL,
D3D_COMPILE_STANDARD_FILE_INCLUDE,
pEntryPoint,
pShaderModel,
0,
0,
ppBlob,
&pErrorMsg
);
if (FAILED(hr))
{
// シェーダのエラー内容を表示
MessageBox(NULL, (char*)pErrorMsg->GetBufferPointer(), "Compile Error", MB_OK);
SafeRelease(pErrorMsg);
return false;
}
return true;
}
// 初期化
bool VertexShader::Initialize(DirectX11& directX, const char* pFilename, const char* pEntryPoint)
{
ID3DBlob* pBlob;
if (!Compile(pFilename, pEntryPoint, "vs_5_0", &pBlob))
{
return false;
}
auto pDevice = directX.GetDevice();
HRESULT hr;
// 頂点シェーダの生成
hr = pDevice->CreateVertexShader(
pBlob->GetBufferPointer(),
pBlob->GetBufferSize(),
NULL,
&m_pShader
);
if (FAILED(hr)) {
SafeRelease(pBlob);
return false;
}
m_pShaderCode = pBlob;
return true;
}
// 終了処理
void VertexShader::Finalize(void)
{
SafeRelease(m_pShaderCode);
Shader::Finalize();
}
// 初期化
bool PixelShader::Initialize(DirectX11& directX, const char* pFilename, const char* pEntryPoint)
{
ID3DBlob* pBlob;
if (!Compile(pFilename, pEntryPoint, "ps_5_0", &pBlob))
{
return false;
}
auto pDevice = directX.GetDevice();
HRESULT hr;
// ピクセルシェーダの生成
hr = pDevice->CreatePixelShader(
pBlob->GetBufferPointer(),
pBlob->GetBufferSize(),
NULL,
&m_pShader
);
SafeRelease(pBlob);
return SUCCEEDED(hr);
}
基底クラスでは解放処理と最初のページで記載したコンパイル処理を記述しています。
コンパイル時に使用するシェーダモデルは種類毎に異なるので各シェーダで指定しています。
頂点シェーダとピクセルシェーダの実装の違いは下記です。
頂点シェーダ
- シェーダモデル:vs_5_0
- 生成処理:ID3DDevice::CreateVertexShader
- シェーダコードの保持
ピクセルシェーダ
- シェーダモデル:ps_5_0
- 生成処理:ID3DDevice::CreatePixelShader
各シェーダで違う部分はこれだけです。
他のシェーダも実装は同様なので基底クラスを使うことで全て簡略化できます。
※後述しますが頂点シェーダだけはシェーダバイナリを保持する必要があります。