Continue(s)

Twitter:@dn0t_ GitHub:@ogrew

【Unity】shader_featureとmulti_compileの話。

vol.1を読んだ勢いでvol.2を読み始めた。 booth.pm

第7章まで読んでShader Variantが登場したので、まとめ記事を上げる前にここだけ別途メモを残しておく。

まず、Variantというのはググると「変異体」とか「変形」みたいな言葉が意味としてまず出てくるけど、理解する上では「多様体」みたいなニュアンスのほうがわかりやすい。1つのシェーダからキーワード(文字通り)を駆使して様々な結果を生み出すようにする仕組み。重要構文としてmulti_compileshader_featureが出てくる。

公式ドキュメントより

多くの場合、固定されたシェーダーコードの断片の大部分を保持するだけでなく、わずかに異なるシェーダー「バリアント」を作成できるようにしておくと便利です。一般に「メガシェーダー」や「ウーバーシェーダー」と呼ばれ、ケースごとに異なるプリプロセッサーディレクティブでシェーダーコードを複数回コンパイルすることで達成されます。 Unity では、これは シェーダースニペットに #pragma multi_compile または #pragma shader_feature を追加することによって達成することができます。これも サーフェースシェーダー で動作します。 実行時には、適切なシェーダーバリアントは、マテリアルのキーワード (Material.EnableKeyword と DisableKeyword) またはグローバルシェーダーキーワード (Shader.EnableKeyword と DisableKeyword ) からピックアップされます。

docs.unity3d.com

multi_compileを使う

Shader "Sample/TestShader"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
...
            #pragma multi_compile _ RED
...
            fixed4 frag(v2f i) : SV_Target
            {
               #ifdef RED
                return fixed4(1, 0, 0, 1);
               #endif
                return fixed4(0, 0, 0, 1);
            }
...

こういうシェーダを用意しておく。 ここでREDというのがまさにキーワードというやつ。それから_ってのはどのキーワードも有効じゃないときのためのもの。switch文のdefault的なもんだと思う。 このキーワードのON,OFFをスクリプトから操作するのは簡単で、

Shader.EnableKeyword("RED");
Shader.DisableKeyword("RED");

あるいは

Material.EnableKeyword("RED");
Material.DisableKeyword("RED");

でできる。(それぞれマテリアルに対してなのか、シェーダに対してなのかが違う。)#ifdef ~ #endif の間に #elif を使えば分岐はもっと増やせる。(#ifndefもある。)

当然、コンパイルの時点で処理されるので、裸の if 構文を使うより効率が良い。 f:id:taiga006:20200416211959g:plain

Propertiesからキーワードを操作する

これはいろいろ手段はあるっぽい。Toggleの例を示す。

Shader "Sample/TestShader"
{
    Properties
    {
        [Toggle(_R)] _R("Red", Float) = 0
        [Toggle(_B)] _B("Blue", Float) = 0
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM

            #pragma vertex   vert
            #pragma fragment frag
            #pragma multi_compile _ _R
            #pragma multi_compile _ _B
...
            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 color = fixed4(0, 0, 0, 1);

               #ifdef _R
                color.r += 1;
               #endif

               #ifdef _B
                color.b += 1;
               #endif

                return color;
            }
...

とかって書くと、 f:id:taiga006:20200416212945g:plain

みたいにできる。

shader_featureを使う

キーワードが増えるとシェーダがどんどん大きくなってしまう。それを避ける方法にshader_featureがある。 これはmulti_compile とほとんど同じだけど、

- _ を省略できる
- 未使用のキーワードで有効になる部分はコンパイル時にシェーダプログラムに含まない

という違いがある。#pragma multi_compile _ _R#pragma shader_feature _R に変えるだけ。

さいごに

とまあ、こんな感じです。Unity Shader Programming Vol.2の第7章のまとめでした。

esprog.hatenablog.com

light11.hatenadiary.com

こちらの2つの記事も似通った内容で、参考になりました。