シンプルなMRTのサンプル。

散々ImageEffectを書いてきたわりに、
MRTについて触れていなかったので簡単なMRTのサンプルコードを置いておきます
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">どうにも自分にとって良い感じのMRTのサンプルが見つけられなかったので実装。<br>動画のキューブだけにメイン(深度),r成分,g成分,b成分を同時出力するシェーダをつけて描画→FrameBufferにコピー。 <a href="https://t.co/ENAXQRH0t9">pic.twitter.com/ENAXQRH0t9</a></p>&mdash; MIYAKE (@ScreenPocket) <a href="https://twitter.com/ScreenPocket/status/1273997677374566400?ref_src=twsrc%5Etfw">June 19, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

今回のサンプルで必要なものは２つ。

- カメラ用のスクリプト
    - 描画用のRenderTextureを作る（今回は４つ）
    - カメラの描画先をその４つのRenderTextureに設定する
    - 描画が終わったらメイン画像を、本来描画するはずだったフレームバッファにコピーする
- MRT描画する用のシェーダ
    - MRTで4つの出力先を使用するので shader target 3.5を指定
    - FragmentShaderの出力先をSV_TargetからColor4つに変更
        - それ用の構造体を定義
        - 色の流し込み

まず、カメラに貼っ付けるコンポーネントのコードから。

```cs:MrtCamera.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

/// <summary>
/// MRTチェック用のカメラ
/// </summary>
[RequireComponent(typeof(Camera))]
public class MrtCamera : MonoBehaviour
{
    /// <summary>
    /// 出力先
    /// </summary>
    private RenderTexture _main;
    private RenderTexture _red;
    private RenderTexture _green;
    private RenderTexture _blue;

    /// <summary>
    /// 確認用
    /// </summary>
    [SerializeField]
    private RawImage[] _rawImages;

    private void Start()
    {
        
        
        var width = Screen.width;
        var height = Screen.height;
        
        //描画先作成（Mainだけ深度有り）
        _main = new RenderTexture(width, height, 24);
        _red = new RenderTexture(width, height, 0);
        _green = new RenderTexture(width, height, 0);
        _blue = new RenderTexture(width, height, 0);

        //描画先設定
        var cam = GetComponent<Camera>();
        cam.SetTargetBuffers(new []
        {
            _main.colorBuffer,
            _red.colorBuffer,
            _green.colorBuffer,
            _blue.colorBuffer
        }, _main.depthBuffer);

        //書き終わったmainをフレームバッファにコピーするコマンドを作成
        var commandBuffer = new CommandBuffer();
        var currentActiveRenderTargetIdentifier = new RenderTargetIdentifier(BuiltinRenderTextureType.CurrentActive);
        commandBuffer.SetRenderTarget (-1);
        commandBuffer.Blit(
            _main,
            currentActiveRenderTargetIdentifier);
        //全部終わった後でコマンド発行
        cam.AddCommandBuffer(CameraEvent.AfterEverything, commandBuffer );

        //確認用RawImageに貼り付ける
        _rawImages[0].texture = _main;
        _rawImages[1].texture = _red;
        _rawImages[2].texture = _green;
        _rawImages[3].texture = _blue;
    }
}
```
_rawImagesの辺りは確認する必要がなかったら除去して下さい。
念の為　SystemInfo.supportedRenderTargetCount　でサポートしているRenderTarget数をチェックするとなお良いかと。

---
オブジェクトに貼り付けて複数の描画先に出力するシェーダはこちら。

```glsl:MrtTest.shader
Shader "ScreenPocket/3d/MrtTest"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #pragma target 3.5

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };
            
            struct mrt
            {
                fixed4 color0 : COLOR0;
                fixed4 color1 : COLOR1;
                fixed4 color2 : COLOR2;
                fixed4 color3 : COLOR3;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            mrt frag (v2f i)
            {
                mrt o;
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                
                o.color0 = col;
                o.color1 = fixed4(col.r,0,0,1);
                o.color2 = fixed4(0,col.g,0,1);
                o.color3 = fixed4(0,0,col.b,1);
                
                return o;
            }
            ENDCG
        }
    }
}

```
Unlitシェーダの改変です。
ポイントは

- #pragma target 3.5を指定
    - targetについてはこちら　→　[シェーダーコンパイルターゲットレベル](https://docs.unity3d.com/ja/current/Manual/SL-ShaderCompileTargets.html)
        
> mrt4: 複数のレンダーターゲット、少なくとも 4
> 一般に言う #pragma target ディレクティブは上の要件を簡略にしたものであり、以下に対応します。
> 3.5: 3.0 + interpolators15 + mrt4 + integers + 2darray + instancing

※なので#pragma require mrt4でも良いかも？

- 出力用にstruct mrtを定義して、それをFragmentShaderの出力先に指定
- o.color0〜3にそれぞれ色成分を出力

---
参考にしたサイトは下記
[1つのカメラでMRT出力を用いたPostEffectを実現する](http://esprog.hatenablog.com/entry/2016/03/31/220601)
[【Unity】カメラ１つでUI解像度を維持し、3D解像度だけを下げる方法](http://appleorbit.hatenablog.com/entry/2018/03/11/193539)