using System; using UnityEngine.Rendering.RenderGraphModule; using System.Runtime.CompilerServices; // AggressiveInlining namespace UnityEngine.Rendering.Universal { internal sealed class MotionBlurPostProcessPass : PostProcessPass { public const string k_TargetName = "_MotionBlurTarget"; Material m_Material; bool m_IsValid; public MotionBlurPostProcessPass(Shader shader) { this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1; this.profilingSampler = null; m_Material = PostProcessUtils.LoadShader(shader, passName); m_IsValid = m_Material != null; } public override void Dispose() { CoreUtils.Destroy(m_Material); m_IsValid = false; } private class MotionBlurPassData { internal TextureHandle sourceTexture; internal Material material; internal int passIndex; internal Camera camera; internal Experimental.Rendering.XRPass xr; internal float intensity; internal float clamp; internal bool enableAlphaOutput; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { if(!m_IsValid) return; var motionBlur = volumeStack.GetComponent(); UniversalCameraData cameraData = frameData.Get(); UniversalResourceData resourceData = frameData.Get(); var sourceTexture = resourceData.cameraColor; var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, sourceTexture, k_TargetName, true, FilterMode.Bilinear); TextureHandle motionVectorColor = resourceData.motionVectorColor; TextureHandle cameraDepthTexture = resourceData.cameraDepthTexture; var mode = motionBlur.mode.value; int passIndex = (int)motionBlur.quality.value; passIndex += (mode == MotionBlurMode.CameraAndObjects) ? ShaderPass.k_CameraAndObjectMotionBlurLow : ShaderPass.k_CameraMotionBlurLow; using (var builder = renderGraph.AddRasterRenderPass("Motion Blur", out var passData, ProfilingSampler.Get(URPProfileId.RG_MotionBlur))) { builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.Write); passData.sourceTexture = sourceTexture; builder.UseTexture(sourceTexture, AccessFlags.Read); if (mode == MotionBlurMode.CameraAndObjects) { Debug.Assert(ScriptableRenderer.current.SupportsMotionVectors(), "Current renderer does not support motion vectors."); Debug.Assert(motionVectorColor.IsValid(), "Motion vectors are invalid. Per-object motion blur requires a motion vector texture."); builder.UseTexture(motionVectorColor, AccessFlags.Read); } Debug.Assert(cameraDepthTexture.IsValid(), "Camera depth texture is invalid. Per-camera motion blur requires a depth texture."); builder.UseTexture(cameraDepthTexture, AccessFlags.Read); passData.material = m_Material; passData.passIndex = passIndex; passData.camera = cameraData.camera; passData.xr = cameraData.xr; passData.enableAlphaOutput = cameraData.isAlphaOutputEnabled; passData.intensity = motionBlur.intensity.value; passData.clamp = motionBlur.clamp.value; builder.SetRenderFunc(static (MotionBlurPassData data, RasterGraphContext context) => { var cmd = context.cmd; RTHandle sourceTextureHdl = data.sourceTexture; UpdateMotionBlurMatrices(data.material, data.camera, data.xr); var sourceSize = PostProcessUtils.CalcShaderSourceSize(sourceTextureHdl); data.material.SetVector(ShaderConstants._SourceSize, sourceSize); data.material.SetFloat(ShaderConstants._Intensity, data.intensity); data.material.SetFloat(ShaderConstants._Clamp, data.clamp); CoreUtils.SetKeyword(data.material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, data.enableAlphaOutput); Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one; Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, data.material, data.passIndex); }); } resourceData.cameraColor = destinationTexture; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UpdateMotionBlurMatrices(Material material, Camera camera, Experimental.Rendering.XRPass xr) { MotionVectorsPersistentData motionData = null; if(camera.TryGetComponent(out var additionalCameraData)) motionData = additionalCameraData.motionVectorsPersistentData; if (motionData == null) return; #if ENABLE_VR && ENABLE_XR_MODULE if (xr.enabled && xr.singlePassEnabled) { // pass maximum of 2 matrices per pass. Need to access into the matrix array var viewStartIndex = xr.viewCount * xr.multipassId; // Using motionData.stagingMatrixStereo as staging buffer to avoid allocation Array.Copy(motionData.previousViewProjectionStereo, viewStartIndex, motionData.stagingMatrixStereo, 0, xr.viewCount); material.SetMatrixArray(ShaderConstants._PrevViewProjMStereo, motionData.stagingMatrixStereo); Array.Copy(motionData.viewProjectionStereo, viewStartIndex, motionData.stagingMatrixStereo, 0, xr.viewCount); material.SetMatrixArray(ShaderConstants._ViewProjMStereo, motionData.stagingMatrixStereo); } else #endif { int viewProjMIdx = 0; #if ENABLE_VR && ENABLE_XR_MODULE if (xr.enabled) viewProjMIdx = xr.multipassId * xr.viewCount; #endif // TODO: These should be part of URP main matrix set. For now, we set them here for motion vector rendering. material.SetMatrix(ShaderConstants._PrevViewProjM, motionData.previousViewProjectionStereo[viewProjMIdx]); material.SetMatrix(ShaderConstants._ViewProjM, motionData.viewProjectionStereo[viewProjMIdx]); } } // Precomputed shader ids to same some CPU cycles (mostly affects mobile) public static class ShaderConstants { public static readonly int _ViewProjM = Shader.PropertyToID("_ViewProjM"); public static readonly int _PrevViewProjM = Shader.PropertyToID("_PrevViewProjM"); public static readonly int _ViewProjMStereo = Shader.PropertyToID("_ViewProjMStereo"); public static readonly int _PrevViewProjMStereo = Shader.PropertyToID("_PrevViewProjMStereo"); public static readonly int _Intensity = Shader.PropertyToID("_Intensity"); public static readonly int _Clamp = Shader.PropertyToID("_Clamp"); public static readonly int _SourceSize = Shader.PropertyToID("_SourceSize"); } public static class ShaderPass { public const int k_CameraMotionBlurLow = 0; public const int k_CameraMotionBlurMedium = 1; public const int k_CameraMotionBlurHigh = 2; public const int k_CameraAndObjectMotionBlurLow = 3; public const int k_CameraAndObjectMotionBlurMedium = 4; public const int k_CameraAndObjectMotionBlurHigh = 5; } } }