こんにちは、Unityエンジニアのオオバです。
シェーダー内で#define マクロ名(引数) 処理
という文法でマクロを書くことが出来ます。
マクロとはCやC++でよく出てくるインラインで展開されるアレです。
UnityCG.cginc
の中身を眺めているとよく出てきます。
眺めることはあっても書いたことはなかったので、試してみました。
またマクロとコンボ技で使用されるマルチコンパイル
についても検証しています。
◆サンプルコード
MacroTest.shader · GitHub
各処理内容を説明していきます。
→11万文字で徹底解説した「DOTweenの教科書」Unityアニメーションの超効率化ツールはこちら
シェーダー側の処理
- 「SHIBUYA24」というキーワードの定義
- 定義したキーワードの有無によるシェーダーを2種類コンパイル
#pragma multi_compile _ SHIBUYA24
↑ここでSHIBUYA24
というキーワードを定義し、SHIBUYA24
キーワードが存在するパターン、存在しないパターンのシェーダーをコンパイルするよう指示しています。
コンパイルされた複数のシェーダーをシェーダーバリアントと呼びます。
このシェーダーバリアントがキーワード定義によって大量に発生し、
- シェーダーコンパイルのCPU負荷増大
- 使用メモリの圧迫問題
これらの問題を引き起こす場合がありますが本記事では割愛します。
◆注意点 1
#pragma multi_compile SHIBUYA24
コメントにも書きましたが、↑このように書いてしまうとSHIBUYA24キーワードが存在するパターンしか
コンパイルされませんので、シェーダーを切り替えるということができなくなります。
#pragma multi_compile _(アンダースコア) キーワード名
という文法で定義する必要があります。
ちなみにキーワードの頭には_(アンダースコア)
が必要です。
◆注意点 2
キーワード名は大文字で記述する必要があります!!(ハマりました)
#pragma multi_compile Hoge・・・・☓ ダメ、キーワードが定義されたことになりません
#pragma multi_compile HOGE・・・・○ 正解
3.SHIBUYA24キーワードの有無によるマクロ挙動の切り替え
#ifdef SHIBUYA24
#define TEST_MACRO(abc) abc.x = 0;
#else
#define TEST_MACRO(abc) abc.z = 0;
#endif
このようにマクロを#define マクロ名(引数) 処理
という文法で定義し、キーワードによる切り替えは#ifdef ~ #endif
で指定します。
マクロの改行
マクロの処理が複数行になる場合、改行したくなります。
以下のように記述するとコンパイルエラーです。
#define TEST_MACRO(abc)
abc.x = 0;
こちらのように\ (バックスラッシュ)
をお尻に付けるとコンパイルが通ります。
#define TEST_MACRO(abc) \
abc.x = 0;
記述要素が2種類以上ある場合は、こんな書き方も可能です。
#define TEST_MACRO(abc) {abc.x = 0; abc.y = 0;}
先と同じように改行したらコンパイルエラーになるので、先のように\
をお尻に付けましょう。
#define Hoge(input) \
{\
input.x = 0;\
input.y = 0;\
}
C#側の処理
キーワードを動的に切り替えてシェーダーを切り替える
if(m_enableMacro)
mat.DisableKeyword("SHIBUYA24");
else
mat.EnableKeyword("SHIBUYA24");
このようにEnableKeyword
、DisableKeyword
メソッドを実行することで、MaterialのShaderを切り替えることが出来ます。

余談
#define TEST_MACRO(abc)
このように処理が何も記述されていないマクロを見かけますが、この場合は何も起きません。ご安心を。
まとめ
マルチコンパイル
とマクロ
を使えば、同じようなシェーダーをいくつも作る必要がなくなり、ファイル構成をスッキリさせられます。
しかし、あまりこれらに頼るとコードの可読性が下がりそうで、何事もバランスが大事だなと思う次第でございます。

この記事が気に入ったらフォローしよう