【Unity】【シェーダ】clip()やdiscardを使うときにはRenderQueueをAlphaTestにする話

AlphaTestというRenderQueueについてちゃんと理解できていない感があったので調べました。

AlphaTestとは?

まずレンダリング順に関するドキュメントを見てみます。

Unity - Manual: ShaderLab: SubShader Tags

これによるとQueueが2500までは不透明描画とのことです。
FrameDebuggerで見るとRender.OpaqueGeometry()というメソッドで描かれているアレですね。

そしてAlphaTestのQueueは2450なので不透明です。
GeometryのQueueは2000なのでAlphaTestのほうが後続の処理になります。
これを踏まえてAlphaTestのドキュメントを見てみます。

Unity - Scripting API: Rendering.RenderQueue.AlphaTest

GeometryとQueueを分けている理由は、不透明なものをすべて描いてからアルファテストのオブジェクトを描画したほうが効率的だからとのこと。
Geometryにアルファテストを行うような処理を書いた場合、他のオブジェクトに遮蔽されている部分では無駄にアルファテストが走っちゃうことがあるからもったいないって理解でよさそうです。

いつ使う?

アルファテストと同様の処理といえばclip()やdiscardです。

DirectX11アルファテストはどこへ? discard/clip命令とAlphaToCoverage | ZeroGram

そのため、clip()、discardを使うシェーダの場合はQueueをAlphaTestにしておけばいいということだと思います。

一応Unityの組み込みシェーダを見てみます。
レガシーシェーダにカットアウト系のものがあるのでこれを見てみます。

Unity - Manual: Transparent Cutout Shader Family

Shader "Unlit/Transparent Cutout" {
Properties {
    _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
    _Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
}
SubShader {
    Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
    LOD 100

    Lighting Off

    Pass {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata_t {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                UNITY_VERTEX_OUTPUT_STEREO
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _Cutoff;

            v2f vert (appdata_t v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.texcoord);
                clip(col.a - _Cutoff);
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
        ENDCG
    }
}

}

QueueはAlphaTestとされていて、フラグメントシェーダではclip()関数が呼ばれています。

というわけでやはり、clip()、discardを使うシェーダの場合はQueueをAlphaTestにしておけばよさそうです。

レガシーな話

ちなみにすでにレガシーなものとなっていますが、Unityにはアルファテストというコマンドがあったようです。

www.shibuya24.info

もう使うことないと思うし読んでないですが、一応。