シェーダー基礎、プロパティにトグルを追加

シェーダー基礎、プロパティにトグルを追加

[Unityシェーダー基礎] Shaderでプロパティにトグルを追加する方法。
lerp関数について。

関連ページ
参考URL
Unityシェーダーでプロパティにfloatを設定する方法は知っていましたが、boolを設定する方法が分かっていませんでした。
調べた結果boolと近い内容は実装できたのでメモ。ついでにlerp関数についても少し解説しています。

※コードが古くなっていたのでURP用に記事を改修しました(2026/03/15)

シェーダートグルを追加

結論から言うと、Unityシェーダーでbooleanは使えないです。
そもそも シェーダープロパティにはintが設定できない ので、なんとなく事前に予想はしていました。
(コードでintを指定は出来ますが、内部的にはfloatとして処理されます)

その代わり、floatを使って疑似的にトグルをプロパティに表示することはできます。
この際、float値 = 0がトグルOff状態、float値 = 1がトグルのOn状態になります。

サンプルとして、
UseSilhouette
というトグルを作成し、これをOnにした場合画像が灰色のシルエットになるシェーダーを作りました。
プロパティにトグルを追加
OffとOnで画像が以下のように切り替わります。
トグルをOnにした結果

以下は今回のサンプルシェーダーのコード全文です。
Shader "UI/TestToggle"
{
    Properties
    {
        
//実際はfloatだがトグル表示にすることで外から見るとboolに見える

        [MaterialToggle] _UseSilhouette("UseSilhouette", float) = 0
    }

    SubShader
    {
        Tags
        {
            "RenderPipeline" = "UniversalPipeline"
            "Queue" = "Transparent" 
        }
        Cull Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha
    
        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"                

            struct Attributes
            {
                half2 uv : TEXCOORD0;
                float4 positionOS : POSITION;
                half4 color : COLOR;
            };

            struct Varyings
            {
                half2 uv : TEXCOORD0;
                float4 positionCS : SV_POSITION;
                half4 color : COLOR;
            };

            
//uGUI Image > Source Imageにアタッチされた画像を参照する

            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);

            
//SRP Batcherを適用

            CBUFFER_START(UnityPerMaterial)            
                
//今回追加したプロパティに対応する変数

                float _UseSilhouette;
            CBUFFER_END            

            Varyings vert (Attributes input)
            {
                Varyings output;
                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.uv = input.uv;
                output.color = input.color;
                return output;
            }

            half4 frag (Varyings input) : SV_Target
            {
                half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
                col *= input.color;

                
//lerp関数を使って色を合成する

                
//第1引数:開始の色(_UseSilhouetteが 0 のときの色)

                
//第2引数:終了の色(_UseSilhouetteが 1 のときの色)

                
//第3引数:割合(0  1 かのスイッチ)

                col.rgb = lerp(col.rgb, half3(0.5, 0.5, 0.5), _UseSilhouette);

                return col;
            }
            ENDHLSL
        }
    }
}

一応トグルはfloatではなくintを使ってもほぼ同じことは出来ます。
        [MaterialToggle] _UseSilhouette("UseSilhouette", int) = 0
            
//今回追加したプロパティに対応する変数

            int _UseSilhouette;
しかしトグルを多用した ウェーブシェーダー でfloatをintに変えて実装した所、2つ以上のトグルをOnにした時に正しく動かなくなりました。
おそらくはトグルのOnOffを計算式に使った場合、バグが発生する可能性が高いです。
公式が推奨するようにトグルはfloatで実装する方が無難な感じがします。

コードを解説

以下の部分がトグル表示に関係するコード。
[MaterialToggle]
という
マテリアルプロパティドロワー
を指定することでトグル表示を可能にしています。
    Properties
    {
        
//実際はfloatだがトグル表示にすることで外から見るとboolに見える

        [MaterialToggle] _UseSilhouette("UseSilhouette", float) = 0
    }

ちなみに
[MaterialToggle]
の代わりに
[Toggle]
を使っても全く同じ挙動になります。
[Toggle]
Shader Variant
という、シェーダー内で静的分岐処理をさせたい時に便利な機能ですが、処理速度は[MaterialToggle]の方が軽量です。
p.61 : シェーダー基礎、UnityでShaderVariantを使ってコード分岐
p.62 : シェーダー基礎、[Toggle]でShaderVariant
今回は静的分岐ではなく単純に「0 or 1」の係数として使っているだけなので、[MaterialToggle]を採用しています。

下のコードでプロパティに対応する変数を宣言しています。
            
//今回追加したプロパティに対応する変数

            float _UseSilhouette;

下のコードがトグルのOnOff状態によって描画を描き分ける処理です。
                
//lerp関数を使って色を合成する

                
//第1引数:開始の色(_UseSilhouetteが 0 のときの色)

                
//第2引数:終了の色(_UseSilhouetteが 1 のときの色)

                
//第3引数:割合(0  1 かのスイッチ)

                col.rgb = lerp(col.rgb, half3(0.5, 0.5, 0.5), _UseSilhouette);

lerp関数

lerp
は線形補完を実行する大変便利な関数です。
第一引数を最小値、第二引数を最大値として、第三引数に0~1の値を代入することでその間の値を返します。

たとえば第三引数に0.5を代入するコードに変更すると、もともとのピクセル色と灰色がちょうど半分ずつ合成された画像が表示されます。
                
//col.rgb = lerp(col.rgb, half3(0.5, 0.5, 0.5), _UseSilhouette);

                col.rgb = lerp(col.rgb, half3(0.5, 0.5, 0.5), 0.5);
線形補完を0.5に

元のコードでは第三引数に_UseSilhouetteをしています。_UseSilhouetteは[MaterialToggle]によって0か1の値しか代入されません。
このため全く元の画像に変更を加えないか、灰色一色に染めるかの二択になります。
0
0