TemporalAntialiasing.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. using System;
  2. namespace UnityEngine.Rendering.PostProcessing
  3. {
  4. /// <summary>
  5. /// This class holds settings for the Temporal Anti-aliasing (TAA) effect.
  6. /// </summary>
  7. [Serializable]
  8. public sealed class TemporalAntialiasing
  9. {
  10. /// <summary>
  11. /// The diameter (in texels) inside which jitter samples are spread. Smaller values result
  12. /// in crisper but more aliased output, while larger values result in more stable but
  13. /// blurrier output.
  14. /// </summary>
  15. [Tooltip("The diameter (in texels) inside which jitter samples are spread. Smaller values result in crisper but more aliased output, while larger values result in more stable, but blurrier, output.")]
  16. [Range(0.1f, 1f)]
  17. public float jitterSpread = 0.75f;
  18. /// <summary>
  19. /// Controls the amount of sharpening applied to the color buffer. High values may introduce
  20. /// dark-border artifacts.
  21. /// </summary>
  22. [Tooltip("Controls the amount of sharpening applied to the color buffer. High values may introduce dark-border artifacts.")]
  23. [Range(0f, 3f)]
  24. public float sharpness = 0.25f;
  25. /// <summary>
  26. /// The blend coefficient for a stationary fragment. Controls the percentage of history
  27. /// sample blended into the final color.
  28. /// </summary>
  29. [Tooltip("The blend coefficient for a stationary fragment. Controls the percentage of history sample blended into the final color.")]
  30. [Range(0f, 0.99f)]
  31. public float stationaryBlending = 0.95f;
  32. /// <summary>
  33. /// The blend coefficient for a fragment with significant motion. Controls the percentage of
  34. /// history sample blended into the final color.
  35. /// </summary>
  36. [Tooltip("The blend coefficient for a fragment with significant motion. Controls the percentage of history sample blended into the final color.")]
  37. [Range(0f, 0.99f)]
  38. public float motionBlending = 0.85f;
  39. // For custom jittered matrices - use at your own risks
  40. public Func<Camera, Vector2, Matrix4x4> jitteredMatrixFunc;
  41. public Vector2 jitter { get; private set; }
  42. enum Pass
  43. {
  44. SolverDilate,
  45. SolverNoDilate
  46. }
  47. readonly RenderTargetIdentifier[] m_Mrt = new RenderTargetIdentifier[2];
  48. bool m_ResetHistory = true;
  49. const int k_SampleCount = 8;
  50. public int sampleIndex { get; private set; }
  51. // Ping-pong between two history textures as we can't read & write the same target in the
  52. // same pass
  53. const int k_NumEyes = 2;
  54. const int k_NumHistoryTextures = 2;
  55. readonly RenderTexture[][] m_HistoryTextures = new RenderTexture[k_NumEyes][];
  56. readonly int[] m_HistoryPingPong = new int [k_NumEyes];
  57. public bool IsSupported()
  58. {
  59. return SystemInfo.supportedRenderTargetCount >= 2
  60. && SystemInfo.supportsMotionVectors
  61. #if !UNITY_2017_3_OR_NEWER
  62. && !RuntimeUtilities.isVREnabled
  63. #endif
  64. && SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2;
  65. }
  66. internal DepthTextureMode GetCameraFlags()
  67. {
  68. return DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
  69. }
  70. internal void ResetHistory()
  71. {
  72. m_ResetHistory = true;
  73. }
  74. Vector2 GenerateRandomOffset()
  75. {
  76. // The variance between 0 and the actual halton sequence values reveals noticeable instability
  77. // in Unity's shadow maps, so we avoid index 0.
  78. var offset = new Vector2(
  79. HaltonSeq.Get((sampleIndex & 1023) + 1, 2) - 0.5f,
  80. HaltonSeq.Get((sampleIndex & 1023) + 1, 3) - 0.5f
  81. );
  82. if (++sampleIndex >= k_SampleCount)
  83. sampleIndex = 0;
  84. return offset;
  85. }
  86. public Matrix4x4 GetJitteredProjectionMatrix(Camera camera)
  87. {
  88. Matrix4x4 cameraProj;
  89. jitter = GenerateRandomOffset();
  90. jitter *= jitterSpread;
  91. if (jitteredMatrixFunc != null)
  92. {
  93. cameraProj = jitteredMatrixFunc(camera, jitter);
  94. }
  95. else
  96. {
  97. cameraProj = camera.orthographic
  98. ? RuntimeUtilities.GetJitteredOrthographicProjectionMatrix(camera, jitter)
  99. : RuntimeUtilities.GetJitteredPerspectiveProjectionMatrix(camera, jitter);
  100. }
  101. jitter = new Vector2(jitter.x / camera.pixelWidth, jitter.y / camera.pixelHeight);
  102. return cameraProj;
  103. }
  104. public void ConfigureJitteredProjectionMatrix(PostProcessRenderContext context)
  105. {
  106. var camera = context.camera;
  107. camera.nonJitteredProjectionMatrix = camera.projectionMatrix;
  108. camera.projectionMatrix = GetJitteredProjectionMatrix(camera);
  109. camera.useJitteredProjectionMatrixForTransparentRendering = false;
  110. }
  111. // TODO: We'll probably need to isolate most of this for SRPs
  112. public void ConfigureStereoJitteredProjectionMatrices(PostProcessRenderContext context)
  113. {
  114. #if UNITY_2017_3_OR_NEWER
  115. var camera = context.camera;
  116. jitter = GenerateRandomOffset();
  117. jitter *= jitterSpread;
  118. for (var eye = Camera.StereoscopicEye.Left; eye <= Camera.StereoscopicEye.Right; eye++)
  119. {
  120. // This saves off the device generated projection matrices as non-jittered
  121. context.camera.CopyStereoDeviceProjectionMatrixToNonJittered(eye);
  122. var originalProj = context.camera.GetStereoNonJitteredProjectionMatrix(eye);
  123. // Currently no support for custom jitter func, as VR devices would need to provide
  124. // original projection matrix as input along with jitter
  125. var jitteredMatrix = RuntimeUtilities.GenerateJitteredProjectionMatrixFromOriginal(context, originalProj, jitter);
  126. context.camera.SetStereoProjectionMatrix(eye, jitteredMatrix);
  127. }
  128. // jitter has to be scaled for the actual eye texture size, not just the intermediate texture size
  129. // which could be double-wide in certain stereo rendering scenarios
  130. jitter = new Vector2(jitter.x / context.screenWidth, jitter.y / context.screenHeight);
  131. camera.useJitteredProjectionMatrixForTransparentRendering = false;
  132. #endif
  133. }
  134. void GenerateHistoryName(RenderTexture rt, int id, PostProcessRenderContext context)
  135. {
  136. rt.name = "Temporal Anti-aliasing History id #" + id;
  137. if (context.stereoActive)
  138. rt.name += " for eye " + context.xrActiveEye;
  139. }
  140. RenderTexture CheckHistory(int id, PostProcessRenderContext context)
  141. {
  142. int activeEye = context.xrActiveEye;
  143. if (m_HistoryTextures[activeEye] == null)
  144. m_HistoryTextures[activeEye] = new RenderTexture[k_NumHistoryTextures];
  145. var rt = m_HistoryTextures[activeEye][id];
  146. if (m_ResetHistory || rt == null || !rt.IsCreated())
  147. {
  148. RenderTexture.ReleaseTemporary(rt);
  149. rt = context.GetScreenSpaceTemporaryRT(0, context.sourceFormat);
  150. GenerateHistoryName(rt, id, context);
  151. rt.filterMode = FilterMode.Bilinear;
  152. m_HistoryTextures[activeEye][id] = rt;
  153. context.command.BlitFullscreenTriangle(context.source, rt);
  154. }
  155. else if (rt.width != context.width || rt.height != context.height)
  156. {
  157. // On size change, simply copy the old history to the new one. This looks better
  158. // than completely discarding the history and seeing a few aliased frames.
  159. var rt2 = context.GetScreenSpaceTemporaryRT(0, context.sourceFormat);
  160. GenerateHistoryName(rt2, id, context);
  161. rt2.filterMode = FilterMode.Bilinear;
  162. m_HistoryTextures[activeEye][id] = rt2;
  163. context.command.BlitFullscreenTriangle(rt, rt2);
  164. RenderTexture.ReleaseTemporary(rt);
  165. }
  166. return m_HistoryTextures[activeEye][id];
  167. }
  168. internal void Render(PostProcessRenderContext context)
  169. {
  170. var sheet = context.propertySheets.Get(context.resources.shaders.temporalAntialiasing);
  171. var cmd = context.command;
  172. cmd.BeginSample("TemporalAntialiasing");
  173. int pp = m_HistoryPingPong[context.xrActiveEye];
  174. var historyRead = CheckHistory(++pp % 2, context);
  175. var historyWrite = CheckHistory(++pp % 2, context);
  176. m_HistoryPingPong[context.xrActiveEye] = ++pp % 2;
  177. const float kMotionAmplification = 100f * 60f;
  178. sheet.properties.SetVector(ShaderIDs.Jitter, jitter);
  179. sheet.properties.SetFloat(ShaderIDs.Sharpness, sharpness);
  180. sheet.properties.SetVector(ShaderIDs.FinalBlendParameters, new Vector4(stationaryBlending, motionBlending, kMotionAmplification, 0f));
  181. sheet.properties.SetTexture(ShaderIDs.HistoryTex, historyRead);
  182. // TODO: Account for different possible RenderViewportScale value from previous frame...
  183. int pass = context.camera.orthographic ? (int)Pass.SolverNoDilate : (int)Pass.SolverDilate;
  184. m_Mrt[0] = context.destination;
  185. m_Mrt[1] = historyWrite;
  186. cmd.BlitFullscreenTriangle(context.source, m_Mrt, context.source, sheet, pass);
  187. cmd.EndSample("TemporalAntialiasing");
  188. m_ResetHistory = false;
  189. }
  190. internal void Release()
  191. {
  192. if (m_HistoryTextures != null)
  193. {
  194. for (int i = 0; i < m_HistoryTextures.Length; i++)
  195. {
  196. if (m_HistoryTextures[i] == null)
  197. continue;
  198. for (int j = 0; j < m_HistoryTextures[i].Length; j++)
  199. {
  200. RenderTexture.ReleaseTemporary(m_HistoryTextures[i][j]);
  201. m_HistoryTextures[i][j] = null;
  202. }
  203. m_HistoryTextures[i] = null;
  204. }
  205. }
  206. sampleIndex = 0;
  207. m_HistoryPingPong[0] = 0;
  208. m_HistoryPingPong[1] = 0;
  209. ResetHistory();
  210. }
  211. }
  212. }