【Unity】わかった気になるMotion Vector

f:id:halya_11:20180821143401p:plain

UnityのMotion Vectorについて、内部的な実装はさておき挙動だけ理解する記事です。

Unity2017.4.6

Motion Vector?

Motion Vectorとは、あるオブジェクトが前フレームからどれだけ移動したかをベクトルで表したものです。
これはモーションブラーやアンチエイリアシング(TemporalAA)といったポストエフェクトなどに利用されます。

Unityではこれをスクリーンスペースのテクスチャとして簡単に取得できます。 RがU方向、GがV方向への移動量を示します。

f:id:halya_11:20180905230636p:plain

フォーマットはRGHalfであり、速度が大きければ1以上の値も入るし、
UV逆方向に進んでいる場合には負の値も入ってきます。

以降、UnityにおけるMotion Vectorの挙動を簡単に見ていきます。

Motion Vectorを有効化する

まずMotion Vectorを有効化してみます。
有効化するには、Camera.depthTextureModeを以下のように変更します。

using UnityEngine;

public class MotionVectorCameraSample : MonoBehaviour {

    private void Start()
    {
        GetComponent<Camera>().depthTextureMode = DepthTextureMode.MotionVectors;
    }
}

これでMotion Vectorがテクスチャにレンダリングされます。
再生してFrameDebuggerを見るとRender.MotionVectorsという処理が追加されていることがわかります。

f:id:halya_11:20180821135007p:plain

Motion Vectorのテクスチャを取得する

次に前節で作ったテクスチャをポストエフェクトのシェーダ内で取得してみます。
Motion Vectorのテクスチャは_CameraMotionVectorsTextureというグローバル変数に入ってきます。

Shader "MotionVectorSample"
{
    Properties{
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Cull Off
        ZTest Always
        ZWrite Off

        Tags { "RenderType"="Opaque" }

        Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
            
           #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            sampler2D _CameraMotionVectorsTexture;
            float4 _CameraMotionVectorsTexture_TexelSize;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // カメラから見たオブジェクトの
                // 右方向へのスピードが大きいほどRの値が大きくなり
                // 上方向へのスピードが大きいほどGの値が大きくなる
                // (1以上の値もとるし逆向きだったら負の値をとる)
                float2 v = tex2D(_CameraMotionVectorsTexture, i.uv).rg;
                v *= _CameraMotionVectorsTexture_TexelSize.zw;
                return half4(v.x, v.y, 0, 1);

            }
            ENDCG
        }
    }
}

これをポストエフェクトとして適用することでテクスチャに格納された値が可視化されますが、
今回の環境ではMotion Vectorの値が大きすぎて見づらいので調整します。

v *= 0.1;

結果は次のようになります。

f:id:halya_11:20180821133622g:plain

U方向に進んでいるときほどRが大きく、V方向に進んでいるときほどGが大きくなっていることが確認できます。

処理負荷について

Motion Vectorを有効にすると、オブジェクト毎にMotion Vector用のレンダリングパスが追加されるため、処理負荷は大きくなります。

ただしRendererのMotion Vectorsモードを切り替えるとオブジェクト毎にMotion Vectorの書き込みを制御できます。

  • Camera Motion Only : Cameraの動きに対するMotion Vectorのみ書き込む(処理負荷低い?)
  • Per Object Motion : オブジェクト毎にレンダリングパスを追加する
  • Force No Motion : Motion Vectorは常にゼロとなる(FrameDebuggerを見るとパスが減るわけではない / ゼロを書き込んでる?)

Unity - Scripting API: MotionVectorGenerationMode

また、浮動小数点テクスチャを使っているため、この点でも処理負荷は大きくなるといえます。

Motion Vectorに対応しているか調べる

対応しているか調べるには次のようにします。

SystemInfo.supportsMotionVectors && RenderTextureFormat.RGHalf

参考

github.com

【Unity】5.4&5.5新機能を深掘り(Motion Vectors編) – 株式会社ロジカルビート