PostProcessingStack v2を使い、前フレームの画像と合成するmotion blurを実装する
1フレーム前の画像を取っておき、それをレンダリング中の画像と合成するpost effectを実装します。
shader
やっている事は単純で、流し込まれた前フレームの画像をアルファ合成しているだけです。
Shader "MotionBlurLegacyHLSL" { HLSLINCLUDE #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl" TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex); TEXTURE2D_SAMPLER2D(_PrevFrameTex, sampler_PrevFrameTex); float _Alpha; #define SAMPLES_INT 6 float4 Frag(VaryingsDefault i) : SV_Target { half4 dst = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord); half4 prevColor = SAMPLE_TEXTURE2D(_PrevFrameTex, sampler_PrevFrameTex, i.texcoord); half4 color = prevColor * _Alpha + dst * (1 - _Alpha); return color; } ENDHLSL SubShader { Cull Off ZWrite Off ZTest Always Pass { HLSLPROGRAM #pragma vertex VertDefault #pragma fragment Frag ENDHLSL } } }
c#
using System; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.PostProcessing; namespace White { [Serializable] [PostProcess(typeof(MotionBlurLegacyRenderer), PostProcessEvent.AfterStack, "Custom/MotionBlurLegacyHLSL", false)] public sealed class MotionBlurLegacy : PostProcessEffectSettings { [Range(0f, 0.99f)] public FloatParameter alpha = new FloatParameter {value = 0f}; public override bool IsEnabledAndSupported(PostProcessRenderContext context) { if (enabled.value) { if (alpha == 0) { return false; } return true; } return false; } } internal sealed class MotionBlurLegacyRenderer : PostProcessEffectRenderer<MotionBlurLegacy> { Shader shader; private RenderTexture _prevFrameRt; private RenderTargetIdentifier _rtId; public override void Init() { float scale = 1; int width = Mathf.CeilToInt(Screen.width * scale); int height = Mathf.CeilToInt(Screen.height * scale); _prevFrameRt = new RenderTexture(width, height, 24, RenderTextureFormat.ARGBHalf); _prevFrameRt.useMipMap = false; _prevFrameRt.filterMode = FilterMode.Bilinear; _prevFrameRt.name = "prev_frame"; _prevFrameRt.Create(); _rtId = new RenderTargetIdentifier(_prevFrameRt); shader = Shader.Find("MotionBlurLegacyHLSL"); } public override void Render(PostProcessRenderContext context) { PropertySheet sheet = context.propertySheets.Get(shader); sheet.properties.SetFloat("_Alpha", settings.alpha); sheet.properties.SetTexture("_PrevFrameTex", _prevFrameRt); context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0); context.command.Blit(null, _rtId); } } }
どうやって前フレームのキャプチャを渡すのか、というのが分かると簡単です。
まず、Initでキャプチャ用のRenderTextureを生成しておきます。 RenderTargetIdentifierを使ってcommandbufferにidで渡せるようにするのがポイント。
そして
context.command.BlitFullscreenTriangle()
した後に、
context.command.Blit(null, _rtId);
してレンダリング結果を保存します。後は
sheet.properties.SetTexture("_PrevFrameTex", _prevFrameRt);
でshaderに渡すだけです。