ScreenSpaceReflections.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. using System;
  2. using UnityEngine.Assertions;
  3. namespace UnityEngine.Rendering.PostProcessing
  4. {
  5. /// <summary>
  6. /// Screen-space Reflections quality presets.
  7. /// </summary>
  8. public enum ScreenSpaceReflectionPreset
  9. {
  10. Lower, Low, Medium, High, Higher, Ultra, Overkill, Custom
  11. }
  12. /// <summary>
  13. /// Screen-space Reflections buffer sizes.
  14. /// </summary>
  15. public enum ScreenSpaceReflectionResolution
  16. {
  17. /// <summary>
  18. /// Downsampled buffer. Faster but lower quality.
  19. /// </summary>
  20. Downsampled,
  21. /// <summary>
  22. /// Full-sized buffer. Slower but higher quality.
  23. /// </summary>
  24. FullSize,
  25. /// <summary>
  26. /// Supersampled buffer. Very slow but much higher quality.
  27. /// </summary>
  28. Supersampled
  29. }
  30. /// <summary>
  31. /// A volume parameter holding a <see cref="ScreenSpaceReflectionPreset"/> value.
  32. /// </summary>
  33. [Serializable]
  34. public sealed class ScreenSpaceReflectionPresetParameter : ParameterOverride<ScreenSpaceReflectionPreset> { }
  35. /// <summary>
  36. /// A volume parameter holding a <see cref="ScreenSpaceReflectionResolution"/> value.
  37. /// </summary>
  38. [Serializable]
  39. public sealed class ScreenSpaceReflectionResolutionParameter : ParameterOverride<ScreenSpaceReflectionResolution> { }
  40. /// <summary>
  41. /// This class holds settings for the Screen-space Reflections effect.
  42. /// </summary>
  43. [Serializable]
  44. [PostProcess(typeof(ScreenSpaceReflectionsRenderer), "Unity/Screen-space reflections")]
  45. public sealed class ScreenSpaceReflections : PostProcessEffectSettings
  46. {
  47. /// <summary>
  48. /// The quality preset to use for rendering. Use <see cref="ScreenSpaceReflectionPreset.Custom"/>
  49. /// to tweak settings.
  50. /// </summary>
  51. [Tooltip("Choose a quality preset, or use \"Custom\" to create your own custom preset. Don't use a preset higher than \"Medium\" if you desire good performance on consoles.")]
  52. public ScreenSpaceReflectionPresetParameter preset = new ScreenSpaceReflectionPresetParameter { value = ScreenSpaceReflectionPreset.Medium };
  53. /// <summary>
  54. /// The maximum number of steps in the raymarching pass. Higher values mean more reflections.
  55. /// </summary>
  56. [Range(0, 256), Tooltip("Maximum number of steps in the raymarching pass. Higher values mean more reflections.")]
  57. public IntParameter maximumIterationCount = new IntParameter { value = 16 };
  58. /// <summary>
  59. /// Changes the size of the internal buffer. Downsample it to maximize performances or
  60. /// supersample it to get slow but higher quality results.
  61. /// </summary>
  62. [Tooltip("Changes the size of the SSR buffer. Downsample it to maximize performances or supersample it for higher quality results with reduced performance.")]
  63. public ScreenSpaceReflectionResolutionParameter resolution = new ScreenSpaceReflectionResolutionParameter { value = ScreenSpaceReflectionResolution.Downsampled };
  64. /// <summary>
  65. /// The ray thickness. Lower values are more expensive but allow the effect to detect
  66. /// smaller details.
  67. /// </summary>
  68. [Range(1f, 64f), Tooltip("Ray thickness. Lower values are more expensive but allow the effect to detect smaller details.")]
  69. public FloatParameter thickness = new FloatParameter { value = 8f };
  70. /// <summary>
  71. /// The maximum distance to traverse in the scene after which it will stop drawing
  72. /// reflections.
  73. /// </summary>
  74. [Tooltip("Maximum distance to traverse after which it will stop drawing reflections.")]
  75. public FloatParameter maximumMarchDistance = new FloatParameter { value = 100f };
  76. /// <summary>
  77. /// Fades reflections close to the near plane. This is useful to hide common artifacts.
  78. /// </summary>
  79. [Range(0f, 1f), Tooltip("Fades reflections close to the near planes.")]
  80. public FloatParameter distanceFade = new FloatParameter { value = 0.5f };
  81. /// <summary>
  82. /// Fades reflections close to the screen edges.
  83. /// </summary>
  84. [Range(0f, 1f), Tooltip("Fades reflections close to the screen edges.")]
  85. public FloatParameter vignette = new FloatParameter { value = 0.5f };
  86. /// <inheritdoc />
  87. public override bool IsEnabledAndSupported(PostProcessRenderContext context)
  88. {
  89. return enabled
  90. && context.camera.actualRenderingPath == RenderingPath.DeferredShading
  91. && SystemInfo.supportsMotionVectors
  92. && SystemInfo.supportsComputeShaders
  93. && SystemInfo.copyTextureSupport > CopyTextureSupport.None
  94. && context.resources.shaders.screenSpaceReflections
  95. && context.resources.shaders.screenSpaceReflections.isSupported
  96. && context.resources.computeShaders.gaussianDownsample;
  97. }
  98. }
  99. internal sealed class ScreenSpaceReflectionsRenderer : PostProcessEffectRenderer<ScreenSpaceReflections>
  100. {
  101. RenderTexture m_Resolve;
  102. RenderTexture m_History;
  103. int[] m_MipIDs;
  104. class QualityPreset
  105. {
  106. public int maximumIterationCount;
  107. public float thickness;
  108. public ScreenSpaceReflectionResolution downsampling;
  109. }
  110. readonly QualityPreset[] m_Presets =
  111. {
  112. new QualityPreset { maximumIterationCount = 10, thickness = 32, downsampling = ScreenSpaceReflectionResolution.Downsampled }, // Lower
  113. new QualityPreset { maximumIterationCount = 16, thickness = 32, downsampling = ScreenSpaceReflectionResolution.Downsampled }, // Low
  114. new QualityPreset { maximumIterationCount = 32, thickness = 16, downsampling = ScreenSpaceReflectionResolution.Downsampled }, // Medium
  115. new QualityPreset { maximumIterationCount = 48, thickness = 8, downsampling = ScreenSpaceReflectionResolution.Downsampled }, // High
  116. new QualityPreset { maximumIterationCount = 16, thickness = 32, downsampling = ScreenSpaceReflectionResolution.FullSize }, // Higher
  117. new QualityPreset { maximumIterationCount = 48, thickness = 16, downsampling = ScreenSpaceReflectionResolution.FullSize }, // Ultra
  118. new QualityPreset { maximumIterationCount = 128, thickness = 12, downsampling = ScreenSpaceReflectionResolution.Supersampled }, // Overkill
  119. };
  120. enum Pass
  121. {
  122. Test,
  123. Resolve,
  124. Reproject,
  125. Composite
  126. }
  127. public override DepthTextureMode GetCameraFlags()
  128. {
  129. return DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
  130. }
  131. internal void CheckRT(ref RenderTexture rt, int width, int height, RenderTextureFormat format, FilterMode filterMode, bool useMipMap)
  132. {
  133. if (rt == null || !rt.IsCreated() || rt.width != width || rt.height != height || rt.format != format)
  134. {
  135. if (rt != null)
  136. {
  137. rt.Release();
  138. RuntimeUtilities.Destroy(rt);
  139. }
  140. rt = new RenderTexture(width, height, 0, format)
  141. {
  142. filterMode = filterMode,
  143. useMipMap = useMipMap,
  144. autoGenerateMips = false,
  145. hideFlags = HideFlags.HideAndDontSave
  146. };
  147. rt.Create();
  148. }
  149. }
  150. public override void Render(PostProcessRenderContext context)
  151. {
  152. var cmd = context.command;
  153. cmd.BeginSample("Screen-space Reflections");
  154. // Get quality settings
  155. if (settings.preset.value != ScreenSpaceReflectionPreset.Custom)
  156. {
  157. int id = (int)settings.preset.value;
  158. settings.maximumIterationCount.value = m_Presets[id].maximumIterationCount;
  159. settings.thickness.value = m_Presets[id].thickness;
  160. settings.resolution.value = m_Presets[id].downsampling;
  161. }
  162. settings.maximumMarchDistance.value = Mathf.Max(0f, settings.maximumMarchDistance.value);
  163. // Square POT target
  164. int size = Mathf.ClosestPowerOfTwo(Mathf.Min(context.width, context.height));
  165. if (settings.resolution.value == ScreenSpaceReflectionResolution.Downsampled)
  166. size >>= 1;
  167. else if (settings.resolution.value == ScreenSpaceReflectionResolution.Supersampled)
  168. size <<= 1;
  169. // The gaussian pyramid compute works in blocks of 8x8 so make sure the last lod has a
  170. // minimum size of 8x8
  171. const int kMaxLods = 12;
  172. int lodCount = Mathf.FloorToInt(Mathf.Log(size, 2f) - 3f);
  173. lodCount = Mathf.Min(lodCount, kMaxLods);
  174. CheckRT(ref m_Resolve, size, size, context.sourceFormat, FilterMode.Trilinear, true);
  175. var noiseTex = context.resources.blueNoise256[0];
  176. var sheet = context.propertySheets.Get(context.resources.shaders.screenSpaceReflections);
  177. sheet.properties.SetTexture(ShaderIDs.Noise, noiseTex);
  178. var screenSpaceProjectionMatrix = new Matrix4x4();
  179. screenSpaceProjectionMatrix.SetRow(0, new Vector4(size * 0.5f, 0f, 0f, size * 0.5f));
  180. screenSpaceProjectionMatrix.SetRow(1, new Vector4(0f, size * 0.5f, 0f, size * 0.5f));
  181. screenSpaceProjectionMatrix.SetRow(2, new Vector4(0f, 0f, 1f, 0f));
  182. screenSpaceProjectionMatrix.SetRow(3, new Vector4(0f, 0f, 0f, 1f));
  183. var projectionMatrix = GL.GetGPUProjectionMatrix(context.camera.projectionMatrix, false);
  184. screenSpaceProjectionMatrix *= projectionMatrix;
  185. sheet.properties.SetMatrix(ShaderIDs.ViewMatrix, context.camera.worldToCameraMatrix);
  186. sheet.properties.SetMatrix(ShaderIDs.InverseViewMatrix, context.camera.worldToCameraMatrix.inverse);
  187. sheet.properties.SetMatrix(ShaderIDs.InverseProjectionMatrix, projectionMatrix.inverse);
  188. sheet.properties.SetMatrix(ShaderIDs.ScreenSpaceProjectionMatrix, screenSpaceProjectionMatrix);
  189. sheet.properties.SetVector(ShaderIDs.Params, new Vector4((float)settings.vignette.value, settings.distanceFade.value, settings.maximumMarchDistance.value, lodCount));
  190. sheet.properties.SetVector(ShaderIDs.Params2, new Vector4((float)context.width / (float)context.height, (float)size / (float)noiseTex.width, settings.thickness.value, settings.maximumIterationCount.value));
  191. cmd.GetTemporaryRT(ShaderIDs.Test, size, size, 0, FilterMode.Point, context.sourceFormat);
  192. cmd.BlitFullscreenTriangle(context.source, ShaderIDs.Test, sheet, (int)Pass.Test);
  193. if (context.isSceneView)
  194. {
  195. cmd.BlitFullscreenTriangle(context.source, m_Resolve, sheet, (int)Pass.Resolve);
  196. }
  197. else
  198. {
  199. CheckRT(ref m_History, size, size, context.sourceFormat, FilterMode.Bilinear, false);
  200. if (m_ResetHistory)
  201. {
  202. context.command.BlitFullscreenTriangle(context.source, m_History);
  203. m_ResetHistory = false;
  204. }
  205. cmd.GetTemporaryRT(ShaderIDs.SSRResolveTemp, size, size, 0, FilterMode.Bilinear, context.sourceFormat);
  206. cmd.BlitFullscreenTriangle(context.source, ShaderIDs.SSRResolveTemp, sheet, (int)Pass.Resolve);
  207. sheet.properties.SetTexture(ShaderIDs.History, m_History);
  208. cmd.BlitFullscreenTriangle(ShaderIDs.SSRResolveTemp, m_Resolve, sheet, (int)Pass.Reproject);
  209. cmd.CopyTexture(m_Resolve, 0, 0, m_History, 0, 0);
  210. cmd.ReleaseTemporaryRT(ShaderIDs.SSRResolveTemp);
  211. }
  212. cmd.ReleaseTemporaryRT(ShaderIDs.Test);
  213. // Pre-cache mipmaps ids
  214. if (m_MipIDs == null || m_MipIDs.Length == 0)
  215. {
  216. m_MipIDs = new int[kMaxLods];
  217. for (int i = 0; i < kMaxLods; i++)
  218. m_MipIDs[i] = Shader.PropertyToID("_SSRGaussianMip" + i);
  219. }
  220. var compute = context.resources.computeShaders.gaussianDownsample;
  221. int kernel = compute.FindKernel("KMain");
  222. var last = new RenderTargetIdentifier(m_Resolve);
  223. for (int i = 0; i < lodCount; i++)
  224. {
  225. size >>= 1;
  226. Assert.IsTrue(size > 0);
  227. cmd.GetTemporaryRT(m_MipIDs[i], size, size, 0, FilterMode.Bilinear, context.sourceFormat, RenderTextureReadWrite.Default, 1, true);
  228. cmd.SetComputeTextureParam(compute, kernel, "_Source", last);
  229. cmd.SetComputeTextureParam(compute, kernel, "_Result", m_MipIDs[i]);
  230. cmd.SetComputeVectorParam(compute, "_Size", new Vector4(size, size, 1f / size, 1f / size));
  231. cmd.DispatchCompute(compute, kernel, size / 8, size / 8, 1);
  232. cmd.CopyTexture(m_MipIDs[i], 0, 0, m_Resolve, 0, i + 1);
  233. last = m_MipIDs[i];
  234. }
  235. for (int i = 0; i < lodCount; i++)
  236. cmd.ReleaseTemporaryRT(m_MipIDs[i]);
  237. sheet.properties.SetTexture(ShaderIDs.Resolve, m_Resolve);
  238. cmd.BlitFullscreenTriangle(context.source, context.destination, sheet, (int)Pass.Composite);
  239. cmd.EndSample("Screen-space Reflections");
  240. }
  241. public override void Release()
  242. {
  243. RuntimeUtilities.Destroy(m_Resolve);
  244. RuntimeUtilities.Destroy(m_History);
  245. m_Resolve = null;
  246. m_History = null;
  247. }
  248. }
  249. }