## 在Unity built-in RP中擷取motion vector
> 記錄一些我自己查不太到資料、曾經被困擾很久的小問題們
這篇文章主要會說明如何從unity取得motion vector並匯出成圖片。
網路上大部分查到的資料會是使用URP或HDRP,但是unity其實有內建的`_CameraMotionVectorsTexture`可以在built-in RP中得到motion vector的資料。
首先需要enable motion vectors和depth才能使用`_CameraMotionVectorsTexture`。
```csharp
motionVectorCamera = GetComponent<Camera>();
motionVectorCamera.depthTextureMode = DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
```
創建一個RenderTexture來保存。
```csharp
currentFrameTexture = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGBFloat);
currentFrameTexture.enableRandomWrite = true;
currentFrameTexture.Create();
```
然後在onRenderImage()
```csharp
private void OnRenderImage(RenderTexture src, RenderTexture dest){
CaptureFrame(src, dest, motionVectorMaterial, PATH);
}
```
```csharp
private void CaptureFrame(RenderTexture src, RenderTexture dest, Material shaderProgram, string filepath){
// render the current frame's render to the new RenderTexture
Graphics.Blit(src, currentFrameTexture, shaderProgram);
// save the frame's render to a Texture2D
Texture2D tex = new Texture2D(currentFrameTexture.width, currentFrameTexture.height, TextureFormat.RGBAFloat, false);
tex.ReadPixels(new Rect(0, 0, currentFrameTexture.width, currentFrameTexture.height), 0, 0);
tex.Apply();
// save those data to exr file
byte[] exrBytes = tex.EncodeToEXR();
System.IO.File.WriteAllBytes(filepath + ".exr", exrBytes);
Destroy(tex);
Graphics.Blit(src, dest);
}
```
這邊是匯出成EXR檔,也可以改成PNG或TXT之類的。
這邊用到了一個shaderProgram的Material,接下來就來看shader的部分:
```shader=
Shader "Custom/MotionVectorShader"
{
Properties
{
_MainTex("Current Frame", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
Cull Off ZWrite Off ZTest Always
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;
sampler2D _CameraMotionVectorsTexture;
float4 _CameraMotionVectorsTexture_TexelSize;
float4 _CameraMotionVectorsTexture_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _CameraMotionVectorsTexture);
return o;
}
half4 frag(v2f i) : SV_Target
{
// Fetch motion vectors from Unity's built-in motion vector buffer
float2 motionVector = tex2D(_CameraMotionVectorsTexture, i.uv).rg;
float2 color = tex2D(_MainTex, i.uv).rg;
// for visualizing
motionVector = motionVector * 10 + 0.5;
return half4(motionVector.x, motionVector.y, 0, 1);
}
ENDCG
}
}
}
```
這樣就成功地從`_CameraMotionVectorsTexture`拿到motion vector的資料了。