シェーダのdefineをcpp側から指定したい(DxLib)
小ネタです
学生さんから質問があったので、ちゃちゃちゃっと解説したいと思います。
前提知識としては、普通にシェーダが使える事ですので、シェーダ自体の使い方が分からない人はもっと初心者向けの記事をまず読んでください。
また、ここでのDXライブラリのDirectXバージョンはDirectX11とします。DirectX9はサポートしていません。
はい、で、例えばMV1のデフォルトVertexShaderとかを見ると、やたらと#ifdefが書かれているのを見ると思います。
#ifdef BUMPMAP
// バンプマップ
float3 Tan : TANGENT0 ; // 接線( ローカル空間 )
float3 Bin : BINORMAL0 ; // 従法線( ローカル空間 )
#endif // BUMPMAP
#ifdef SKINMESH
// スキニングメッシュ
int4 BlendIndices0 : BLENDINDICES0 ; // ボーン処理用 Float型定数配列インデックス0
float4 BlendWeight0 : BLENDWEIGHT0 ; // ボーン処理用ウエイト値0
#ifdef BONE8
int4 BlendIndices1 : BLENDINDICES1 ; // ボーン処理用 Float型定数配列インデックス1
float4 BlendWeight1 : BLENDWEIGHT1 ; // ボーン処理用ウエイト値1
#endif // BONE8
#endif // SKINMESH
こんなやつ
なるほど、C++みたいに#defineや#ifdefや#elseや#endifが使えるというわけですね。
でもでも、あれれ?
そういえば#defineがないよ?どこで定義してるの?#include?
どこにもありませーん!
これ自体はDXライブラリ内で指定されてるモノなのでコントロールできないんですよね。
また、C++の#defineと同様に「プリプロセッサ」であるため、いったんコンパイル済みのVertexShader.vsoやらPixelShader.pso状態になると、もう定義できないんですよね~。
でも、設定によっていちいち新しくvsoやpsoを作るのは面倒っすよね。
じゃあ、どうするのかというとLoadVertexShaderFromMem,LoadPixelShaderFromMem,という関数を使用します。
こいつはメモリ上にある「コンパイル済みのバイナリだか中間言語だか(vsoやpsoのなかみ)」を読み取って、頂点シェーダやピクセルシェーダとして使用できるハンドルを得る関数です。
では、それをどうやって取得するのかというと、実行時にコンパイルしてしまうのです。
しかしそんな関数はDXライブラリにはありません。
外から持ってきます。
#include<d3dcompiler.h>
をインクルードし、
#pragma comment(lib, "d3dcompiler.lib")
でリンクします。
で、この中にある関数
D3DCompileFromFile
https://learn.microsoft.com/ja-jp/windows/win32/api/d3dcompiler/nf-d3dcompiler-d3dcompilefromfile
を使用します。使い方はとっても簡単!と言いたいのですが、DXライブラリしか使った事ない人にとってはちょっと難しいかもしれません。
細かい引数の説明は前述のリファレンスを見ていただくとして、このように使います。
D3D_SHADER_MACRO defines[] = {
{ "DEFINE_TESTED", "1" }, // テスト出力
{ nullptr, nullptr }
};
ID3DBlob* blobPS = nullptr;
ID3DBlob* blobErr = nullptr;
HRESULT result = D3DCompileFromFile(L"Shader/MV1PixelShader.hlsl", defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, "main", "ps_5_0", 0, 0, &blobPS,&blobErr);
内容は
D3DCompileFromFile(ファイル名,
defineマクロ配列,
インクルードポインタ,
エントリポイント,
シェーダバージョン,
パラメータ1,
パラメータ2,
コンパイル済みバイナリ受取ポインタ,
エラー時のエラー情報受取ポインタ);
引数多いですね、イヤになります
ともかくこれでコンパイル済みのシェーダデータがblobPSに入るわけです。あとはこれをLoadPixelShaderFromMemに、よし、じゃあぶちこんでやるZE
int psH = LoadPixelShaderFromMem(blobPS->GetBufferPointer(), blobPS->GetBufferSize());
assert(psH >= 0);
なお、blobPSはただのデータの塊なので、GetBufferPointer()は先頭アドレス、GetBufferSize()はそのバッファのサイズを返します。
ここまでできたら、あとはいつもどおりです。
で、しれっと書きましたが、今回のテーマは「シェーダのdefineをcpp側から指定したい」でしたよね?
実はすでに指定しています。こ↑こ↓
D3D_SHADER_MACRO defines[] = {
{ "DEFINE_TESTED", "1" }, // テスト出力
{ nullptr, nullptr }
};
これは
https://learn.microsoft.com/ja-jp/windows/win32/api/d3dcommon/ns-d3dcommon-d3d_shader_macro
に書かれてあるように、define名と、その内容を指定するものです。正直ifdefに使うだけなら、名前の部分だけでOKだと思います。
で、これはサイズを入れられないので、最後には必ずnullptr,nullptrの指定を入れてください。
これで、cpp側で指定したdefineが実行時コンパイル時にhlsl側につたわりまs