【Unity】不透明描画のオーバードローを正確に表示する方法

Unityで不透明描画のオーバードローを正確に表示する方法をまとめました。

Unity2019.2.5

SceneビューのOverdrawの問題点

UnityのSceneビューにはオーバードローを見ることができる機能があります。

f:id:halya_11:20190919165824p:plain

この機能は処理負荷を見るときに便利です。
しかしながら、このOverDrawモードは半透明描画のシェーダを使っているので、
不透明描画のオーバードローが正確に表示できていないという問題があります。

https://forum.unity.com/threads/overdraw-of-opaque-and-transparent-geometry.463962/

たとえば次のように不透明なオブジェクトを4つ重ねて配置します。

f:id:halya_11:20190919170240p:plain

この状態でSceneビューをOverdrawモードにすると以下のような表示になります。

f:id:halya_11:20190919170300p:plain

不透明なので本当はZテストにより重ねて描画されていないにもかかわらず、
重ねて描画されているかのような表示になってしまっています。

正確に不透明描画のオーバードローを表示する

正確に不透明描画のオーバーには、シェーダとスクリプトを書く必要があります。
まずZライトとZテストを行い、薄めの色を加算するシェーダを書きます。

Shader "OverDrawOpaque"
{
    CGINCLUDE

    #pragma vertex vert
    #pragma fragment frag

    struct appdata
    {
        float4 vertex : POSITION;
    };

    struct v2f
    {
        float4 vertex : SV_POSITION;
    };

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        return fixed4(0.1, 0.03, 0.03, 1);
    }

    ENDCG

    SubShader
    {
        Tags{ "RenderType" = "Opaque" }
        
        Pass
        {
            ZWrite On
            ZTest LEqual
            Blend One One
            Cull Back

            CGPROGRAM
            ENDCG
        }
    }
}

一点注意点として、今回は標準的な不透明シェーダを想定してカリングの設定をCull Backとしています。
もしカリングをオフにしたい場合にはこれをCull Offと書き換える必要があります。

そしてReplaced Shaderを使って描画対象のシェーダをこれに置換します。

using UnityEngine;

[RequireComponent(typeof(Camera))]
public class OverDrawOpaque : MonoBehaviour
{
    private Camera _camera;
    private CameraClearFlags _preClearFlags;
    private Color _preBackgroundColor;

    private void OnEnable()
    {
        _camera = GetComponent<Camera>();
        if (_camera != null) {
            _preClearFlags = _camera.clearFlags;
            _preBackgroundColor = _camera.backgroundColor;
            _camera.SetReplacementShader(Shader.Find("OverDrawOpaque"), "RenderType");
            _camera.clearFlags = CameraClearFlags.SolidColor;
            _camera.backgroundColor = Color.black;
        }
    }

    private void OnDisable()
    {
        _camera?.ResetReplacementShader();
        if (_camera != null) {
            _camera.clearFlags = _preClearFlags;
            _camera.backgroundColor = _preBackgroundColor;
        }
    }
}

Replaced Shaderの使い方は下記にまとめているので必要に応じて参照してください。

light11.hatenadiary.com

使い方

あとはこのスクリプトをCameraにアタッチするだけで、Gameビューに不透明オブジェクトのオーバードロー情報が表示されます。

f:id:halya_11:20190919171103p:plain

重なり部分のオーバードローが正確に表示できるようになったことが確認できます。
ちなみに今回は不透明なオブジェクト以外は描画しないようにしています。

関連

light11.hatenadiary.com