MotionBlurComponent.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. using UnityEngine.Rendering;
  2. namespace UnityEngine.PostProcessing
  3. {
  4. using Settings = MotionBlurModel.Settings;
  5. public sealed class MotionBlurComponent : PostProcessingComponentCommandBuffer<MotionBlurModel>
  6. {
  7. static class Uniforms
  8. {
  9. internal static readonly int _VelocityScale = Shader.PropertyToID("_VelocityScale");
  10. internal static readonly int _MaxBlurRadius = Shader.PropertyToID("_MaxBlurRadius");
  11. internal static readonly int _RcpMaxBlurRadius = Shader.PropertyToID("_RcpMaxBlurRadius");
  12. internal static readonly int _VelocityTex = Shader.PropertyToID("_VelocityTex");
  13. internal static readonly int _MainTex = Shader.PropertyToID("_MainTex");
  14. internal static readonly int _Tile2RT = Shader.PropertyToID("_Tile2RT");
  15. internal static readonly int _Tile4RT = Shader.PropertyToID("_Tile4RT");
  16. internal static readonly int _Tile8RT = Shader.PropertyToID("_Tile8RT");
  17. internal static readonly int _TileMaxOffs = Shader.PropertyToID("_TileMaxOffs");
  18. internal static readonly int _TileMaxLoop = Shader.PropertyToID("_TileMaxLoop");
  19. internal static readonly int _TileVRT = Shader.PropertyToID("_TileVRT");
  20. internal static readonly int _NeighborMaxTex = Shader.PropertyToID("_NeighborMaxTex");
  21. internal static readonly int _LoopCount = Shader.PropertyToID("_LoopCount");
  22. internal static readonly int _TempRT = Shader.PropertyToID("_TempRT");
  23. internal static readonly int _History1LumaTex = Shader.PropertyToID("_History1LumaTex");
  24. internal static readonly int _History2LumaTex = Shader.PropertyToID("_History2LumaTex");
  25. internal static readonly int _History3LumaTex = Shader.PropertyToID("_History3LumaTex");
  26. internal static readonly int _History4LumaTex = Shader.PropertyToID("_History4LumaTex");
  27. internal static readonly int _History1ChromaTex = Shader.PropertyToID("_History1ChromaTex");
  28. internal static readonly int _History2ChromaTex = Shader.PropertyToID("_History2ChromaTex");
  29. internal static readonly int _History3ChromaTex = Shader.PropertyToID("_History3ChromaTex");
  30. internal static readonly int _History4ChromaTex = Shader.PropertyToID("_History4ChromaTex");
  31. internal static readonly int _History1Weight = Shader.PropertyToID("_History1Weight");
  32. internal static readonly int _History2Weight = Shader.PropertyToID("_History2Weight");
  33. internal static readonly int _History3Weight = Shader.PropertyToID("_History3Weight");
  34. internal static readonly int _History4Weight = Shader.PropertyToID("_History4Weight");
  35. }
  36. enum Pass
  37. {
  38. VelocitySetup,
  39. TileMax1,
  40. TileMax2,
  41. TileMaxV,
  42. NeighborMax,
  43. Reconstruction,
  44. FrameCompression,
  45. FrameBlendingChroma,
  46. FrameBlendingRaw
  47. }
  48. public class ReconstructionFilter
  49. {
  50. // Texture format for storing 2D vectors.
  51. RenderTextureFormat m_VectorRTFormat = RenderTextureFormat.RGHalf;
  52. // Texture format for storing packed velocity/depth.
  53. RenderTextureFormat m_PackedRTFormat = RenderTextureFormat.ARGB2101010;
  54. public ReconstructionFilter()
  55. {
  56. CheckTextureFormatSupport();
  57. }
  58. void CheckTextureFormatSupport()
  59. {
  60. // If 2:10:10:10 isn't supported, use ARGB32 instead.
  61. if (!SystemInfo.SupportsRenderTextureFormat(m_PackedRTFormat))
  62. m_PackedRTFormat = RenderTextureFormat.ARGB32;
  63. }
  64. public bool IsSupported()
  65. {
  66. return SystemInfo.supportsMotionVectors;
  67. }
  68. public void ProcessImage(PostProcessingContext context, CommandBuffer cb, ref Settings settings, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material)
  69. {
  70. const float kMaxBlurRadius = 5f;
  71. // Calculate the maximum blur radius in pixels.
  72. int maxBlurPixels = (int)(kMaxBlurRadius * context.height / 100);
  73. // Calculate the TileMax size.
  74. // It should be a multiple of 8 and larger than maxBlur.
  75. int tileSize = ((maxBlurPixels - 1) / 8 + 1) * 8;
  76. // Pass 1 - Velocity/depth packing
  77. var velocityScale = settings.shutterAngle / 360f;
  78. cb.SetGlobalFloat(Uniforms._VelocityScale, velocityScale);
  79. cb.SetGlobalFloat(Uniforms._MaxBlurRadius, maxBlurPixels);
  80. cb.SetGlobalFloat(Uniforms._RcpMaxBlurRadius, 1f / maxBlurPixels);
  81. int vbuffer = Uniforms._VelocityTex;
  82. cb.GetTemporaryRT(vbuffer, context.width, context.height, 0, FilterMode.Point, m_PackedRTFormat, RenderTextureReadWrite.Linear);
  83. cb.Blit((Texture)null, vbuffer, material, (int)Pass.VelocitySetup);
  84. // Pass 2 - First TileMax filter (1/2 downsize)
  85. int tile2 = Uniforms._Tile2RT;
  86. cb.GetTemporaryRT(tile2, context.width / 2, context.height / 2, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear);
  87. cb.SetGlobalTexture(Uniforms._MainTex, vbuffer);
  88. cb.Blit(vbuffer, tile2, material, (int)Pass.TileMax1);
  89. // Pass 3 - Second TileMax filter (1/2 downsize)
  90. int tile4 = Uniforms._Tile4RT;
  91. cb.GetTemporaryRT(tile4, context.width / 4, context.height / 4, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear);
  92. cb.SetGlobalTexture(Uniforms._MainTex, tile2);
  93. cb.Blit(tile2, tile4, material, (int)Pass.TileMax2);
  94. cb.ReleaseTemporaryRT(tile2);
  95. // Pass 4 - Third TileMax filter (1/2 downsize)
  96. int tile8 = Uniforms._Tile8RT;
  97. cb.GetTemporaryRT(tile8, context.width / 8, context.height / 8, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear);
  98. cb.SetGlobalTexture(Uniforms._MainTex, tile4);
  99. cb.Blit(tile4, tile8, material, (int)Pass.TileMax2);
  100. cb.ReleaseTemporaryRT(tile4);
  101. // Pass 5 - Fourth TileMax filter (reduce to tileSize)
  102. var tileMaxOffs = Vector2.one * (tileSize / 8f - 1f) * -0.5f;
  103. cb.SetGlobalVector(Uniforms._TileMaxOffs, tileMaxOffs);
  104. cb.SetGlobalFloat(Uniforms._TileMaxLoop, (int)(tileSize / 8f));
  105. int tile = Uniforms._TileVRT;
  106. cb.GetTemporaryRT(tile, context.width / tileSize, context.height / tileSize, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear);
  107. cb.SetGlobalTexture(Uniforms._MainTex, tile8);
  108. cb.Blit(tile8, tile, material, (int)Pass.TileMaxV);
  109. cb.ReleaseTemporaryRT(tile8);
  110. // Pass 6 - NeighborMax filter
  111. int neighborMax = Uniforms._NeighborMaxTex;
  112. int neighborMaxWidth = context.width / tileSize;
  113. int neighborMaxHeight = context.height / tileSize;
  114. cb.GetTemporaryRT(neighborMax, neighborMaxWidth, neighborMaxHeight, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear);
  115. cb.SetGlobalTexture(Uniforms._MainTex, tile);
  116. cb.Blit(tile, neighborMax, material, (int)Pass.NeighborMax);
  117. cb.ReleaseTemporaryRT(tile);
  118. // Pass 7 - Reconstruction pass
  119. cb.SetGlobalFloat(Uniforms._LoopCount, Mathf.Clamp(settings.sampleCount / 2, 1, 64));
  120. cb.SetGlobalTexture(Uniforms._MainTex, source);
  121. cb.Blit(source, destination, material, (int)Pass.Reconstruction);
  122. cb.ReleaseTemporaryRT(vbuffer);
  123. cb.ReleaseTemporaryRT(neighborMax);
  124. }
  125. }
  126. public class FrameBlendingFilter
  127. {
  128. struct Frame
  129. {
  130. public RenderTexture lumaTexture;
  131. public RenderTexture chromaTexture;
  132. float m_Time;
  133. RenderTargetIdentifier[] m_MRT;
  134. public float CalculateWeight(float strength, float currentTime)
  135. {
  136. if (Mathf.Approximately(m_Time, 0f))
  137. return 0f;
  138. var coeff = Mathf.Lerp(80f, 16f, strength);
  139. return Mathf.Exp((m_Time - currentTime) * coeff);
  140. }
  141. public void Release()
  142. {
  143. if (lumaTexture != null)
  144. RenderTexture.ReleaseTemporary(lumaTexture);
  145. if (chromaTexture != null)
  146. RenderTexture.ReleaseTemporary(chromaTexture);
  147. lumaTexture = null;
  148. chromaTexture = null;
  149. }
  150. public void MakeRecord(CommandBuffer cb, RenderTargetIdentifier source, int width, int height, Material material)
  151. {
  152. Release();
  153. lumaTexture = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.R8, RenderTextureReadWrite.Linear);
  154. chromaTexture = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.R8, RenderTextureReadWrite.Linear);
  155. lumaTexture.filterMode = FilterMode.Point;
  156. chromaTexture.filterMode = FilterMode.Point;
  157. if (m_MRT == null)
  158. m_MRT = new RenderTargetIdentifier[2];
  159. m_MRT[0] = lumaTexture;
  160. m_MRT[1] = chromaTexture;
  161. cb.SetGlobalTexture(Uniforms._MainTex, source);
  162. cb.SetRenderTarget(m_MRT, lumaTexture);
  163. cb.DrawMesh(GraphicsUtils.quad, Matrix4x4.identity, material, 0, (int)Pass.FrameCompression);
  164. m_Time = Time.time;
  165. }
  166. public void MakeRecordRaw(CommandBuffer cb, RenderTargetIdentifier source, int width, int height, RenderTextureFormat format)
  167. {
  168. Release();
  169. lumaTexture = RenderTexture.GetTemporary(width, height, 0, format);
  170. lumaTexture.filterMode = FilterMode.Point;
  171. cb.SetGlobalTexture(Uniforms._MainTex, source);
  172. cb.Blit(source, lumaTexture);
  173. m_Time = Time.time;
  174. }
  175. }
  176. bool m_UseCompression;
  177. RenderTextureFormat m_RawTextureFormat;
  178. Frame[] m_FrameList;
  179. int m_LastFrameCount;
  180. public FrameBlendingFilter()
  181. {
  182. m_UseCompression = CheckSupportCompression();
  183. m_RawTextureFormat = GetPreferredRenderTextureFormat();
  184. m_FrameList = new Frame[4];
  185. }
  186. public void Dispose()
  187. {
  188. foreach (var frame in m_FrameList)
  189. frame.Release();
  190. }
  191. public void PushFrame(CommandBuffer cb, RenderTargetIdentifier source, int width, int height, Material material)
  192. {
  193. // Push only when actual update (do nothing while pausing)
  194. var frameCount = Time.frameCount;
  195. if (frameCount == m_LastFrameCount) return;
  196. // Update the frame record.
  197. var index = frameCount % m_FrameList.Length;
  198. if (m_UseCompression)
  199. m_FrameList[index].MakeRecord(cb, source, width, height, material);
  200. else
  201. m_FrameList[index].MakeRecordRaw(cb, source, width, height, m_RawTextureFormat);
  202. m_LastFrameCount = frameCount;
  203. }
  204. public void BlendFrames(CommandBuffer cb, float strength, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material)
  205. {
  206. var t = Time.time;
  207. var f1 = GetFrameRelative(-1);
  208. var f2 = GetFrameRelative(-2);
  209. var f3 = GetFrameRelative(-3);
  210. var f4 = GetFrameRelative(-4);
  211. cb.SetGlobalTexture(Uniforms._History1LumaTex, f1.lumaTexture);
  212. cb.SetGlobalTexture(Uniforms._History2LumaTex, f2.lumaTexture);
  213. cb.SetGlobalTexture(Uniforms._History3LumaTex, f3.lumaTexture);
  214. cb.SetGlobalTexture(Uniforms._History4LumaTex, f4.lumaTexture);
  215. cb.SetGlobalTexture(Uniforms._History1ChromaTex, f1.chromaTexture);
  216. cb.SetGlobalTexture(Uniforms._History2ChromaTex, f2.chromaTexture);
  217. cb.SetGlobalTexture(Uniforms._History3ChromaTex, f3.chromaTexture);
  218. cb.SetGlobalTexture(Uniforms._History4ChromaTex, f4.chromaTexture);
  219. cb.SetGlobalFloat(Uniforms._History1Weight, f1.CalculateWeight(strength, t));
  220. cb.SetGlobalFloat(Uniforms._History2Weight, f2.CalculateWeight(strength, t));
  221. cb.SetGlobalFloat(Uniforms._History3Weight, f3.CalculateWeight(strength, t));
  222. cb.SetGlobalFloat(Uniforms._History4Weight, f4.CalculateWeight(strength, t));
  223. cb.SetGlobalTexture(Uniforms._MainTex, source);
  224. cb.Blit(source, destination, material, m_UseCompression ? (int)Pass.FrameBlendingChroma : (int)Pass.FrameBlendingRaw);
  225. }
  226. // Check if the platform has the capability of compression.
  227. static bool CheckSupportCompression()
  228. {
  229. return
  230. SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.R8) &&
  231. SystemInfo.supportedRenderTargetCount > 1;
  232. }
  233. // Determine which 16-bit render texture format is available.
  234. static RenderTextureFormat GetPreferredRenderTextureFormat()
  235. {
  236. RenderTextureFormat[] formats =
  237. {
  238. RenderTextureFormat.RGB565,
  239. RenderTextureFormat.ARGB1555,
  240. RenderTextureFormat.ARGB4444
  241. };
  242. foreach (var f in formats)
  243. if (SystemInfo.SupportsRenderTextureFormat(f)) return f;
  244. return RenderTextureFormat.Default;
  245. }
  246. // Retrieve a frame record with relative indexing.
  247. // Use a negative index to refer to previous frames.
  248. Frame GetFrameRelative(int offset)
  249. {
  250. var index = (Time.frameCount + m_FrameList.Length + offset) % m_FrameList.Length;
  251. return m_FrameList[index];
  252. }
  253. }
  254. ReconstructionFilter m_ReconstructionFilter;
  255. public ReconstructionFilter reconstructionFilter
  256. {
  257. get
  258. {
  259. if (m_ReconstructionFilter == null)
  260. m_ReconstructionFilter = new ReconstructionFilter();
  261. return m_ReconstructionFilter;
  262. }
  263. }
  264. FrameBlendingFilter m_FrameBlendingFilter;
  265. public FrameBlendingFilter frameBlendingFilter
  266. {
  267. get
  268. {
  269. if (m_FrameBlendingFilter == null)
  270. m_FrameBlendingFilter = new FrameBlendingFilter();
  271. return m_FrameBlendingFilter;
  272. }
  273. }
  274. bool m_FirstFrame = true;
  275. public override bool active
  276. {
  277. get
  278. {
  279. var settings = model.settings;
  280. return model.enabled
  281. && ((settings.shutterAngle > 0f && reconstructionFilter.IsSupported()) || settings.frameBlending > 0f)
  282. && SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2 // No movecs on GLES2 platforms
  283. && !context.interrupted;
  284. }
  285. }
  286. public override string GetName()
  287. {
  288. return "Motion Blur";
  289. }
  290. public void ResetHistory()
  291. {
  292. if (m_FrameBlendingFilter != null)
  293. m_FrameBlendingFilter.Dispose();
  294. m_FrameBlendingFilter = null;
  295. }
  296. public override DepthTextureMode GetCameraFlags()
  297. {
  298. return DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
  299. }
  300. public override CameraEvent GetCameraEvent()
  301. {
  302. return CameraEvent.BeforeImageEffects;
  303. }
  304. public override void OnEnable()
  305. {
  306. m_FirstFrame = true;
  307. }
  308. public override void PopulateCommandBuffer(CommandBuffer cb)
  309. {
  310. #if UNITY_EDITOR
  311. // Don't render motion blur preview when the editor is not playing as it can in some
  312. // cases results in ugly artifacts (i.e. when resizing the game view).
  313. if (!Application.isPlaying)
  314. return;
  315. #endif
  316. // Skip rendering in the first frame as motion vectors won't be abvailable until the
  317. // next one
  318. if (m_FirstFrame)
  319. {
  320. m_FirstFrame = false;
  321. return;
  322. }
  323. var material = context.materialFactory.Get("Hidden/Post FX/Motion Blur");
  324. var blitMaterial = context.materialFactory.Get("Hidden/Post FX/Blit");
  325. var settings = model.settings;
  326. var fbFormat = context.isHdr
  327. ? RenderTextureFormat.DefaultHDR
  328. : RenderTextureFormat.Default;
  329. int tempRT = Uniforms._TempRT;
  330. cb.GetTemporaryRT(tempRT, context.width, context.height, 0, FilterMode.Point, fbFormat);
  331. if (settings.shutterAngle > 0f && settings.frameBlending > 0f)
  332. {
  333. // Motion blur + frame blending
  334. reconstructionFilter.ProcessImage(context, cb, ref settings, BuiltinRenderTextureType.CameraTarget, tempRT, material);
  335. frameBlendingFilter.BlendFrames(cb, settings.frameBlending, tempRT, BuiltinRenderTextureType.CameraTarget, material);
  336. frameBlendingFilter.PushFrame(cb, tempRT, context.width, context.height, material);
  337. }
  338. else if (settings.shutterAngle > 0f)
  339. {
  340. // No frame blending
  341. cb.SetGlobalTexture(Uniforms._MainTex, BuiltinRenderTextureType.CameraTarget);
  342. cb.Blit(BuiltinRenderTextureType.CameraTarget, tempRT, blitMaterial, 0);
  343. reconstructionFilter.ProcessImage(context, cb, ref settings, tempRT, BuiltinRenderTextureType.CameraTarget, material);
  344. }
  345. else if (settings.frameBlending > 0f)
  346. {
  347. // Frame blending only
  348. cb.SetGlobalTexture(Uniforms._MainTex, BuiltinRenderTextureType.CameraTarget);
  349. cb.Blit(BuiltinRenderTextureType.CameraTarget, tempRT, blitMaterial, 0);
  350. frameBlendingFilter.BlendFrames(cb, settings.frameBlending, tempRT, BuiltinRenderTextureType.CameraTarget, material);
  351. frameBlendingFilter.PushFrame(cb, tempRT, context.width, context.height, material);
  352. }
  353. // Cleaning up
  354. cb.ReleaseTemporaryRT(tempRT);
  355. }
  356. public override void OnDisable()
  357. {
  358. if (m_FrameBlendingFilter != null)
  359. m_FrameBlendingFilter.Dispose();
  360. }
  361. }
  362. }