神様は有休消化中です。

Unity関連の技術ネタを書いてます。

【Unity】マテリアルの描画がシェーダーのQueue順に処理されない原因と対策

自作シェーダーなどを使用してレンダリングを行う場合、レンダリング順序をTagsのQueueで指定しますが、特定の条件でこのレンダリング順序が無視される現象が発生したので共有。

簡潔にまとめだけ

まとめると
現象  :シェーダーで指定したレンダリングキューが無視されることがある
原因  :MaterialのCustom Render Queueに不正値が入っているため
発生条件:自作シェーダーを使用 & 自作シェーダー設定前に、他のシェーダーを割り当てていた場合
対策  :Custom Render Queueに-1を設定すればOK


おさらい レンダリング順序について

レンダリング順序は、シェーダーのTags Queueに設定された以下の順になります(なにも指定しない場合は"Geometry")。

Tags{ "Queue"="Geometry" }

・Background
・Geometry(デフォルト)
・AlphaTest
・Transparent
・Overlay

現象

しかし、特定の状況でこのQueueが無視される状況が発生します。
例えば以下の画像では、奥のチェック柄にはUnlit-Colorシェーダー(Queue=Geometry)を、手前のキューブには自作の半透明シェーダー(Queue=Transparent)を設定していますが、キューブが半端に描画されてしまい、カメラ角度によっては完全に消えてしまいます。
f:id:appleorbit:20151118002021p:plain

ちなみに自作シェーダーの中身はこんな感じです。QueueにTransparentを設定し、ZTestにAlways(深度チェックは常にOK)を指定しています。シェーダー的には、完全に描画されるはずです。

Shader "Custom/Alpha Blended" {
Properties {
    _Color ("Main Color", Color) = (1,1,1,1)
    _Alpha ("Alpha", Range(0, 1)) = 1
}

SubShader {
        Tags {
            "Queue"="Transparent"
            "RenderType"="Transparent"
        }

        Blend SrcAlpha OneMinusSrcAlpha

        Lighting Off
        ZWrite Off
        ZTest Always
        Fog { Color (0,0,0,0) }

        CGPROGRAM
              #pragma surface surf Lambert

              fixed _Alpha;
              float4 _Color;

              struct Input {
                  float4 color : COLOR;
              };

            void surf (Input IN, inout SurfaceOutput o) {
                o.Albedo = _Color.rgb;
                o.Alpha = _Alpha;
            }
        ENDCG
    }
}  

原因

結論から書くと、MaterialのCustom Render Queueが原因です。
Materialを選択した状態で、Inspectorをデバッグモードで開きます。デバッグモードは、以下の場所から開けます。
f:id:appleorbit:20151118003422j:plain

表示された項目の、Custom Render Queueに注目してください。下図だと、2000と入力されています。
f:id:appleorbit:20151118003633j:plain

実はこのCustom Render Queueがシェーダーで指定するQueueに対応していて、unityはこの数字に従ってレンダリングを行っている様子です。
Background 1000
Geometry 2000
AlphaTest 2450
Transparent 3000
Overlay 4000

何故自作シェーダーはQueue=TransparentなのにCustom Render Queueが2000になっているかというと、この自作シェーダーを設定する前に、MaterialにUnlit-Colorを設定していたからです。自作シェーダーを設定する場合、自動でCustom Render Queueを更新してくれない様子。。。

対策

対策は簡単で、Custom Render Queueに-1を設定すれば、シェーダーに書いたQueue通りに動作します。
f:id:appleorbit:20151118004850p:plain



いや、はまったはまった。Unityはこれがあるから怖い。