MotionVectorRendering.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. namespace UnityEngine.Rendering.Universal.Internal
  4. {
  5. sealed class MotionVectorRendering
  6. {
  7. #region Fields
  8. static MotionVectorRendering s_Instance;
  9. Dictionary<Camera, PreviousFrameData> m_CameraFrameData;
  10. uint m_FrameCount;
  11. float m_LastTime;
  12. float m_Time;
  13. #endregion
  14. #region Constructors
  15. private MotionVectorRendering()
  16. {
  17. m_CameraFrameData = new Dictionary<Camera, PreviousFrameData>();
  18. }
  19. public static MotionVectorRendering instance
  20. {
  21. get
  22. {
  23. if (s_Instance == null)
  24. s_Instance = new MotionVectorRendering();
  25. return s_Instance;
  26. }
  27. }
  28. #endregion
  29. #region RenderPass
  30. public void Clear()
  31. {
  32. m_CameraFrameData.Clear();
  33. }
  34. public PreviousFrameData GetMotionDataForCamera(Camera camera, CameraData camData)
  35. {
  36. // Get MotionData
  37. PreviousFrameData motionData;
  38. if (!m_CameraFrameData.TryGetValue(camera, out motionData))
  39. {
  40. motionData = new PreviousFrameData();
  41. m_CameraFrameData.Add(camera, motionData);
  42. }
  43. // Calculate motion data
  44. CalculateTime();
  45. UpdateMotionData(camera, camData, motionData);
  46. return motionData;
  47. }
  48. #endregion
  49. void CalculateTime()
  50. {
  51. // Get data
  52. float t = Time.realtimeSinceStartup;
  53. // SRP.Render() can be called several times per frame.
  54. // Also, most Time variables do not consistently update in the Scene View.
  55. // This makes reliable detection of the start of the new frame VERY hard.
  56. // One of the exceptions is 'Time.realtimeSinceStartup'.
  57. // Therefore, outside of the Play Mode we update the time at 60 fps,
  58. // and in the Play Mode we rely on 'Time.frameCount'.
  59. bool newFrame;
  60. #if UNITY_EDITOR
  61. if (!Application.isPlaying)
  62. {
  63. newFrame = (t - m_Time) > 0.0166f;
  64. m_FrameCount += newFrame ? 1u : 0u;
  65. }
  66. else
  67. #endif
  68. {
  69. uint frameCount = (uint)Time.frameCount;
  70. newFrame = m_FrameCount != frameCount;
  71. m_FrameCount = frameCount;
  72. }
  73. if (newFrame)
  74. {
  75. // Make sure both are never 0.
  76. m_LastTime = (m_Time > 0) ? m_Time : t;
  77. m_Time = t;
  78. }
  79. }
  80. void UpdateMotionData(Camera camera, CameraData cameraData, PreviousFrameData motionData)
  81. {
  82. // The actual projection matrix used in shaders is actually massaged a bit to work across all platforms
  83. // (different Z value ranges etc.)
  84. // A camera could be rendered multiple times per frame, only updates the previous view proj & pos if needed
  85. #if ENABLE_VR && ENABLE_XR_MODULE
  86. if (cameraData.xr.enabled)
  87. {
  88. var gpuVP0 = GL.GetGPUProjectionMatrix(cameraData.GetProjectionMatrix(0), true) * cameraData.GetViewMatrix(0);
  89. var gpuVP1 = GL.GetGPUProjectionMatrix(cameraData.GetProjectionMatrix(1), true) * cameraData.GetViewMatrix(1);
  90. // Last frame data
  91. if (motionData.lastFrameActive != Time.frameCount)
  92. {
  93. bool firstFrame = motionData.isFirstFrame;
  94. var prevViewProjStereo = motionData.previousViewProjectionMatrixStereo;
  95. prevViewProjStereo[0] = firstFrame ? gpuVP0 : prevViewProjStereo[0];
  96. prevViewProjStereo[1] = firstFrame ? gpuVP1 : prevViewProjStereo[1];
  97. motionData.isFirstFrame = false;
  98. }
  99. // Current frame data
  100. var viewProjStereo = motionData.viewProjectionMatrixStereo;
  101. viewProjStereo[0] = gpuVP0;
  102. viewProjStereo[1] = gpuVP1;
  103. }
  104. else
  105. #endif
  106. {
  107. var gpuProj = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true); // Had to change this from 'false'
  108. var gpuView = camera.worldToCameraMatrix;
  109. var gpuVP = gpuProj * gpuView;
  110. // Last frame data
  111. if (motionData.lastFrameActive != Time.frameCount)
  112. {
  113. motionData.previousViewProjectionMatrix = motionData.isFirstFrame ? gpuVP : motionData.viewProjectionMatrix;
  114. motionData.isFirstFrame = false;
  115. }
  116. // Current frame data
  117. motionData.viewProjectionMatrix = gpuVP;
  118. }
  119. motionData.lastFrameActive = Time.frameCount;
  120. }
  121. }
  122. }