using System; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.RenderGraphModule; namespace UnityEngine.Rendering.Universal { sealed class MotionVectorRenderPass : ScriptableRenderPass { internal const string k_MotionVectorTextureName = "_MotionVectorTexture"; internal const string k_MotionVectorDepthTextureName = "_MotionVectorDepthTexture"; internal const GraphicsFormat k_TargetFormat = GraphicsFormat.R16G16_SFloat; public const string k_MotionVectorsLightModeTag = "MotionVectors"; static readonly string[] s_ShaderTags = new string[] { k_MotionVectorsLightModeTag }; static readonly int s_CameraDepthTextureID = Shader.PropertyToID("_CameraDepthTexture"); static readonly ProfilingSampler s_SetMotionMatrixProfilingSampler = new ProfilingSampler("Set Motion Vector Global Matrices"); readonly Material m_CameraMaterial; readonly FilteringSettings m_FilteringSettings; internal MotionVectorRenderPass(RenderPassEvent evt, Material cameraMaterial, LayerMask opaqueLayerMask) { profilingSampler = ProfilingSampler.Get(URPProfileId.DrawMotionVectors); renderPassEvent = evt; m_CameraMaterial = cameraMaterial; m_FilteringSettings = new FilteringSettings(RenderQueueRange.opaque,opaqueLayerMask); ConfigureInput(ScriptableRenderPassInput.Depth); } private static void ExecutePass(RasterCommandBuffer cmd, PassData passData, RendererList rendererList) { var cameraMaterial = passData.cameraMaterial; if (cameraMaterial == null) return; // Get data Camera camera = passData.camera; // Never draw in Preview if (camera.cameraType == CameraType.Preview) return; // These flags are still required in SRP or the engine won't compute previous model matrices... // If the flag hasn't been set yet on this camera, motion vectors will skip a frame. camera.depthTextureMode |= DepthTextureMode.MotionVectors | DepthTextureMode.Depth; // TODO: add option to only draw either one? DrawCameraMotionVectors(cmd, passData.xr, cameraMaterial); DrawObjectMotionVectors(cmd, passData.xr, ref rendererList); } private static DrawingSettings GetDrawingSettings(Camera camera, bool supportsDynamicBatching) { var sortingSettings = new SortingSettings(camera) { criteria = SortingCriteria.CommonOpaque }; var drawingSettings = new DrawingSettings(ShaderTagId.none, sortingSettings) { perObjectData = PerObjectData.MotionVectors, enableDynamicBatching = supportsDynamicBatching, enableInstancing = true, lodCrossFadeStencilMask = 0, // Disable stencil-based lod because depth copy before motion vector pass doesn't copy stencils. }; for (int i = 0; i < s_ShaderTags.Length; ++i) { drawingSettings.SetShaderPassName(i, new ShaderTagId(s_ShaderTags[i])); } return drawingSettings; } // NOTE: depends on camera depth to reconstruct static geometry positions private static void DrawCameraMotionVectors(RasterCommandBuffer cmd, XRPass xr, Material cameraMaterial) { #if ENABLE_VR && ENABLE_XR_MODULE bool foveatedRendering = xr.supportsFoveatedRendering; bool nonUniformFoveatedRendering = foveatedRendering && XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster); if (foveatedRendering) { if (nonUniformFoveatedRendering) // This is a screen-space pass, make sure foveated rendering is disabled for non-uniform renders cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled); else cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Enabled); } #endif // Draw fullscreen quad cmd.DrawProcedural(Matrix4x4.identity, cameraMaterial, 0, MeshTopology.Triangles, 3, 1); #if ENABLE_VR && ENABLE_XR_MODULE if (foveatedRendering && !nonUniformFoveatedRendering) cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled); #endif } private static void DrawObjectMotionVectors(RasterCommandBuffer cmd, XRPass xr, ref RendererList rendererList) { #if ENABLE_VR && ENABLE_XR_MODULE bool foveatedRendering = xr.supportsFoveatedRendering; if (foveatedRendering) // This is a geometry pass, enable foveated rendering (we need to disable it after) cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Enabled); #endif cmd.DrawRendererList(rendererList); #if ENABLE_VR && ENABLE_XR_MODULE if (foveatedRendering) cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled); #endif } /// /// Shared pass data /// private class PassData { internal Camera camera; internal XRPass xr; internal TextureHandle cameraDepth; internal Material cameraMaterial; internal RendererListHandle rendererListHdl; // Required for code sharing purpose between RG and non-RG. internal RendererList rendererList; } /// /// Initialize the shared pass data. /// /// private void InitPassData(ref PassData passData, UniversalCameraData cameraData) { passData.camera = cameraData.camera; passData.xr = cameraData.xr; passData.cameraMaterial = m_CameraMaterial; } private void InitRendererLists(ref PassData passData, ref CullingResults cullResults, bool supportsDynamicBatching, RenderGraph renderGraph) { var drawingSettings = GetDrawingSettings(passData.camera, supportsDynamicBatching); var renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, ref cullResults, drawingSettings, m_FilteringSettings, renderStateBlock, ref passData.rendererListHdl); } internal void Render(RenderGraph renderGraph, ContextContainer frameData, TextureHandle cameraDepthTexture, TextureHandle motionVectorColor, TextureHandle motionVectorDepth) { UniversalRenderingData renderingData = frameData.Get(); UniversalCameraData cameraData = frameData.Get(); using (var builder = renderGraph.AddRasterRenderPass(passName, out var passData, profilingSampler)) { builder.UseAllGlobalTextures(true); builder.AllowGlobalStateModification(true); if (cameraData.xr.enabled) { builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && cameraData.xrUniversal.canFoveateIntermediatePasses); // Apply MultiviewRenderRegionsCompatible flag only to the peripheral view in Quad Views if (cameraData.xr.multipassId == 0) { builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible); } } builder.SetRenderAttachment(motionVectorColor, 0, AccessFlags.Write); builder.SetRenderAttachmentDepth(motionVectorDepth, AccessFlags.ReadWrite); InitPassData(ref passData, cameraData); passData.cameraDepth = cameraDepthTexture; builder.UseTexture(cameraDepthTexture, AccessFlags.Read); InitRendererLists(ref passData, ref renderingData.cullResults, renderingData.supportsDynamicBatching, renderGraph); builder.UseRendererList(passData.rendererListHdl); if (motionVectorColor.IsValid()) builder.SetGlobalTextureAfterPass(motionVectorColor, Shader.PropertyToID(k_MotionVectorTextureName)); if (motionVectorDepth.IsValid()) builder.SetGlobalTextureAfterPass(motionVectorDepth, Shader.PropertyToID(k_MotionVectorDepthTextureName)); builder.SetRenderFunc(static (PassData data, RasterGraphContext context) => { if (data.cameraMaterial != null) data.cameraMaterial.SetTexture(s_CameraDepthTextureID, data.cameraDepth); ExecutePass(context.cmd, data, data.rendererListHdl); }); } } // Global motion vector matrix setup pass. // Used for MotionVector passes and also read in VFX early compute shader public class MotionMatrixPassData { public MotionVectorsPersistentData motionData; public XRPass xr; } internal static void SetRenderGraphMotionVectorGlobalMatrices(RenderGraph renderGraph, UniversalCameraData cameraData) { if (cameraData.camera.TryGetComponent(out var additionalCameraData)) { using (var builder = renderGraph.AddRasterRenderPass(s_SetMotionMatrixProfilingSampler.name, out var passData, s_SetMotionMatrixProfilingSampler)) { passData.motionData = additionalCameraData.motionVectorsPersistentData; passData.xr = cameraData.xr; builder.AllowGlobalStateModification(true); builder.SetRenderFunc(static (MotionMatrixPassData data, RasterGraphContext context) => { data.motionData.SetGlobalMotionMatrices(context.cmd, data.xr); }); } } } } }