こんにちは、Unityエンジニアのオオバです。

シェーダー内で#define マクロ名(引数) 処理という文法でマクロを書くことが出来ます。
マクロとはCやC++でよく出てくるインラインで展開されるアレです。

UnityCG.cgincの中身を眺めているとよく出てきます。

眺めることはあっても書いたことはなかったので、試してみました。
またマクロとコンボ技で使用されるマルチコンパイルについても検証しています。

◆サンプルコード
MacroTest.shader · GitHub

各処理内容を説明していきます。

👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

シェーダー側の処理

  1. 「SHIBUYA24」というキーワードの定義
  2. 定義したキーワードの有無によるシェーダーを2種類コンパイル
#pragma multi_compile _ SHIBUYA24

↑ここでSHIBUYA24というキーワードを定義し、SHIBUYA24キーワードが存在するパターン、存在しないパターンのシェーダーをコンパイルするよう指示しています。

コンパイルされた複数のシェーダーをシェーダーバリアントと呼びます。
このシェーダーバリアントがキーワード定義によって大量に発生し、

これらの問題を引き起こす場合がありますが本記事では割愛します。

◆注意点 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");  

このようにEnableKeywordDisableKeywordメソッドを実行することで、MaterialのShaderを切り替えることが出来ます。

Unityシェーダーのマクロとマルチコンパイル_0

余談

#define TEST_MACRO(abc)

このように処理が何も記述されていないマクロを見かけますが、この場合は何も起きません。ご安心を。

まとめ

マルチコンパイルマクロを使えば、同じようなシェーダーをいくつも作る必要がなくなり、ファイル構成をスッキリさせられます。

しかし、あまりこれらに頼るとコードの可読性が下がりそうで、何事もバランスが大事だなと思う次第でございます。

オススメ記事