VolumetricFogRenderFeature.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. //------------------------------------------------------------------------------------------------------------------
  2. // Volumetric Fog & Mist 2
  3. // Created by Kronnect
  4. //------------------------------------------------------------------------------------------------------------------
  5. using System.Collections.Generic;
  6. using UnityEngine;
  7. using UnityEngine.Rendering;
  8. using UnityEngine.Rendering.Universal;
  9. namespace VolumetricFogAndMist2 {
  10. public class VolumetricFogRenderFeature : ScriptableRendererFeature {
  11. public static class ShaderParams {
  12. public const string LightBufferName = "_LightBuffer";
  13. public static int LightBuffer = Shader.PropertyToID(LightBufferName);
  14. public static int LightBufferSize = Shader.PropertyToID("_VFRTSize");
  15. public static int MainTex = Shader.PropertyToID("_MainTex");
  16. public static int BlurRT = Shader.PropertyToID("_BlurTex");
  17. public static int BlurRT2 = Shader.PropertyToID("_BlurTex2");
  18. public static int MiscData = Shader.PropertyToID("_MiscData");
  19. public static int ForcedInvisible = Shader.PropertyToID("_ForcedInvisible");
  20. public static int DownsampledDepth = Shader.PropertyToID("_DownsampledDepth");
  21. public static int BlueNoiseTexture = Shader.PropertyToID("_BlueNoise");
  22. public static int BlurScale = Shader.PropertyToID("_BlurScale");
  23. public static int Downscaling = Shader.PropertyToID("_Downscaling");
  24. public static int ScatteringData = Shader.PropertyToID("_ScatteringData");
  25. public static int ScatteringTint = Shader.PropertyToID("_ScatteringTint");
  26. public static int BlurredTex = Shader.PropertyToID("_BlurredTex");
  27. public const string SKW_DITHER = "DITHER";
  28. public const string SKW_EDGE_PRESERVE = "EDGE_PRESERVE";
  29. public const string SKW_EDGE_PRESERVE_UPSCALING = "EDGE_PRESERVE_UPSCALING";
  30. public const string SKW_SCATTERING_HQ = "SCATTERING_HQ";
  31. }
  32. public static int GetScaledSize(int size, float factor) {
  33. size = (int)(size / factor);
  34. size /= 2;
  35. if (size < 1)
  36. size = 1;
  37. return size * 2;
  38. }
  39. class VolumetricFogRenderPass : ScriptableRenderPass {
  40. FilteringSettings filteringSettings = new FilteringSettings(RenderQueueRange.transparent, -1);
  41. readonly List<ShaderTagId> shaderTagIdList = new List<ShaderTagId>();
  42. const string m_ProfilerTag = "Volumetric Fog Light Buffer Rendering";
  43. RTHandle m_LightBuffer;
  44. VolumetricFogRenderFeature settings;
  45. public VolumetricFogRenderPass() {
  46. shaderTagIdList.Clear();
  47. shaderTagIdList.Add(new ShaderTagId("UniversalForward"));
  48. RenderTargetIdentifier lightBuffer = new RenderTargetIdentifier(ShaderParams.LightBuffer, 0, CubemapFace.Unknown, -1);
  49. m_LightBuffer = RTHandles.Alloc(lightBuffer, name: ShaderParams.LightBufferName);
  50. }
  51. public void CleanUp() {
  52. RTHandles.Release(m_LightBuffer);
  53. }
  54. public void Setup(VolumetricFogRenderFeature settings) {
  55. this.settings = settings;
  56. renderPassEvent = settings.renderPassEvent;
  57. }
  58. public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) {
  59. RenderTextureDescriptor lightBufferDesc = cameraTextureDescriptor;
  60. VolumetricFogManager manager = VolumetricFogManager.GetManagerIfExists();
  61. if (manager != null) {
  62. if (manager.downscaling > 1f) {
  63. int size = GetScaledSize(cameraTextureDescriptor.width, manager.downscaling);
  64. lightBufferDesc.width = size;
  65. lightBufferDesc.height = size;
  66. }
  67. lightBufferDesc.colorFormat = manager.blurHDR ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
  68. cmd.SetGlobalVector(ShaderParams.LightBufferSize, new Vector4(lightBufferDesc.width, lightBufferDesc.height, manager.downscaling > 1f ? 1f: 0, 0));
  69. }
  70. lightBufferDesc.depthBufferBits = 0;
  71. lightBufferDesc.msaaSamples = 1;
  72. lightBufferDesc.useMipMap = false;
  73. cmd.GetTemporaryRT(ShaderParams.LightBuffer, lightBufferDesc, FilterMode.Bilinear);
  74. ConfigureTarget(m_LightBuffer);
  75. ConfigureClear(ClearFlag.Color, new Color(0, 0, 0, 0));
  76. ConfigureInput(ScriptableRenderPassInput.Depth);
  77. }
  78. public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) {
  79. VolumetricFogManager manager = VolumetricFogManager.GetManagerIfExists();
  80. CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);
  81. cmd.SetGlobalInt(ShaderParams.ForcedInvisible, 0);
  82. context.ExecuteCommandBuffer(cmd);
  83. if (manager == null || (manager.downscaling <= 1f && manager.blurPasses < 1 && manager.scattering <= 0)) {
  84. CommandBufferPool.Release(cmd);
  85. return;
  86. }
  87. foreach (VolumetricFog vg in VolumetricFog.volumetricFogs) {
  88. if (vg != null) {
  89. vg.meshRenderer.renderingLayerMask = VolumetricFogManager.FOG_VOLUMES_RENDERING_LAYER;
  90. }
  91. }
  92. cmd.Clear();
  93. var sortFlags = SortingCriteria.CommonTransparent;
  94. var drawSettings = CreateDrawingSettings(shaderTagIdList, ref renderingData, sortFlags);
  95. var filterSettings = filteringSettings;
  96. filterSettings.layerMask = settings.fogLayerMask;
  97. filterSettings.renderingLayerMask = VolumetricFogManager.FOG_VOLUMES_RENDERING_LAYER;
  98. context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSettings);
  99. CommandBufferPool.Release(cmd);
  100. }
  101. /// Cleanup any allocated resources that were created during the execution of this render pass.
  102. public override void FrameCleanup(CommandBuffer cmd) {
  103. }
  104. }
  105. class BlurRenderPass : ScriptableRenderPass {
  106. enum Pass {
  107. BlurHorizontal = 0,
  108. BlurVertical = 1,
  109. BlurVerticalAndBlend = 2,
  110. UpscalingBlend = 3,
  111. DownscaleDepth = 4,
  112. BlurVerticalFinal = 5,
  113. Resample = 6,
  114. ResampleAndCombine = 7,
  115. ScatteringPrefilter = 8,
  116. ScatteringBlend = 9
  117. }
  118. ScriptableRenderer renderer;
  119. Material mat;
  120. RenderTextureDescriptor sourceDesc;
  121. VolumetricFogManager manager;
  122. public void Setup(Shader shader, ScriptableRenderer renderer, VolumetricFogRenderFeature settings) {
  123. this.renderPassEvent = settings.renderPassEvent;
  124. this.renderer = renderer;
  125. this.manager = VolumetricFogManager.GetManagerIfExists();
  126. if (mat == null) {
  127. mat = CoreUtils.CreateEngineMaterial(shader);
  128. Texture2D noiseTex = Resources.Load<Texture2D>("Textures/blueNoiseVF128");
  129. mat.SetTexture(ShaderParams.BlueNoiseTexture, noiseTex);
  130. }
  131. }
  132. public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) {
  133. sourceDesc = cameraTextureDescriptor;
  134. ConfigureInput(ScriptableRenderPassInput.Depth);
  135. }
  136. public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) {
  137. if (manager == null || (manager.downscaling <= 1f && manager.blurPasses < 1 && manager.scattering <= 0)) {
  138. Cleanup();
  139. return;
  140. }
  141. mat.SetVector(ShaderParams.MiscData, new Vector4(manager.ditherStrength * 0.1f, 0, manager.blurEdgeDepthThreshold, manager.downscalingEdgeDepthThreshold * 0.001f));
  142. if (manager.ditherStrength > 0) {
  143. mat.EnableKeyword(ShaderParams.SKW_DITHER);
  144. } else {
  145. mat.DisableKeyword(ShaderParams.SKW_DITHER);
  146. }
  147. mat.DisableKeyword(ShaderParams.SKW_EDGE_PRESERVE);
  148. mat.DisableKeyword(ShaderParams.SKW_EDGE_PRESERVE_UPSCALING);
  149. if (manager.blurPasses > 0 && manager.blurEdgePreserve) {
  150. mat.EnableKeyword(manager.downscaling > 1f ? ShaderParams.SKW_EDGE_PRESERVE_UPSCALING : ShaderParams.SKW_EDGE_PRESERVE);
  151. }
  152. #if UNITY_2022_1_OR_NEWER
  153. RTHandle source = renderer.cameraColorTargetHandle;
  154. #else
  155. RenderTargetIdentifier source = renderer.cameraColorTarget;
  156. #endif
  157. var cmd = CommandBufferPool.Get("Volumetric Fog Render Feature");
  158. cmd.SetGlobalInt(ShaderParams.ForcedInvisible, 1);
  159. RenderTextureDescriptor rtBlurDesc = sourceDesc;
  160. rtBlurDesc.width = GetScaledSize(sourceDesc.width, manager.downscaling);
  161. rtBlurDesc.height = GetScaledSize(sourceDesc.height, manager.downscaling);
  162. rtBlurDesc.useMipMap = false;
  163. rtBlurDesc.colorFormat = manager.blurHDR ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
  164. rtBlurDesc.msaaSamples = 1;
  165. rtBlurDesc.depthBufferBits = 0;
  166. bool usingDownscaling = manager.downscaling > 1f;
  167. if (usingDownscaling) {
  168. RenderTextureDescriptor rtDownscaledDepth = rtBlurDesc;
  169. rtDownscaledDepth.colorFormat = RenderTextureFormat.RFloat;
  170. cmd.GetTemporaryRT(ShaderParams.DownsampledDepth, rtDownscaledDepth, FilterMode.Bilinear);
  171. FullScreenBlit(cmd, source, ShaderParams.DownsampledDepth, mat, (int)Pass.DownscaleDepth);
  172. }
  173. if (manager.blurPasses < 1) {
  174. // no blur but downscaling
  175. FullScreenBlit(cmd, ShaderParams.LightBuffer, source, mat, (int)Pass.UpscalingBlend);
  176. } else {
  177. // blur (with or without downscaling)
  178. rtBlurDesc.width = GetScaledSize(sourceDesc.width, manager.blurDownscaling);
  179. rtBlurDesc.height = GetScaledSize(sourceDesc.height, manager.blurDownscaling);
  180. cmd.GetTemporaryRT(ShaderParams.BlurRT, rtBlurDesc, FilterMode.Bilinear);
  181. cmd.GetTemporaryRT(ShaderParams.BlurRT2, rtBlurDesc, FilterMode.Bilinear);
  182. cmd.SetGlobalFloat(ShaderParams.BlurScale, manager.blurSpread * manager.blurDownscaling);
  183. FullScreenBlit(cmd, ShaderParams.LightBuffer, ShaderParams.BlurRT, mat, (int)Pass.BlurHorizontal);
  184. cmd.SetGlobalFloat(ShaderParams.BlurScale, manager.blurSpread);
  185. for (int k = 0; k < manager.blurPasses - 1; k++) {
  186. FullScreenBlit(cmd, ShaderParams.BlurRT, ShaderParams.BlurRT2, mat, (int)Pass.BlurVertical);
  187. FullScreenBlit(cmd, ShaderParams.BlurRT2, ShaderParams.BlurRT, mat, (int)Pass.BlurHorizontal);
  188. }
  189. if (usingDownscaling) {
  190. FullScreenBlit(cmd, ShaderParams.BlurRT, ShaderParams.BlurRT2, mat, (int)Pass.BlurVerticalFinal);
  191. FullScreenBlit(cmd, ShaderParams.BlurRT2, source, mat, (int)Pass.UpscalingBlend);
  192. } else {
  193. FullScreenBlit(cmd, ShaderParams.BlurRT, source, mat, (int)Pass.BlurVerticalAndBlend);
  194. }
  195. cmd.ReleaseTemporaryRT(ShaderParams.BlurRT2);
  196. cmd.ReleaseTemporaryRT(ShaderParams.BlurRT);
  197. }
  198. if (manager.scattering > 0) {
  199. ComputeScattering(cmd, source, mat);
  200. }
  201. cmd.ReleaseTemporaryRT(ShaderParams.LightBuffer);
  202. if (usingDownscaling) {
  203. cmd.ReleaseTemporaryRT(ShaderParams.DownsampledDepth);
  204. }
  205. context.ExecuteCommandBuffer(cmd);
  206. CommandBufferPool.Release(cmd);
  207. }
  208. struct ScatteringMipData {
  209. public int rtDown, rtUp, width, height;
  210. }
  211. ScatteringMipData[] rt;
  212. const int PYRAMID_MAX_LEVELS = 5;
  213. #if UNITY_2022_1_OR_NEWER
  214. void ComputeScattering(CommandBuffer cmd, RTHandle source, Material mat) {
  215. #else
  216. void ComputeScattering(CommandBuffer cmd, RenderTargetIdentifier source, Material mat) {
  217. #endif
  218. mat.SetVector(ShaderParams.ScatteringData, new Vector4(manager.scatteringThreshold, manager.scatteringIntensity, 1f - manager.scatteringAbsorption, manager.scattering));
  219. mat.SetColor(ShaderParams.ScatteringTint, manager.scatteringTint);
  220. float downscaling = manager.downscaling;
  221. // Initialize buffers descriptors
  222. if (rt == null || rt.Length != PYRAMID_MAX_LEVELS + 1) {
  223. rt = new ScatteringMipData[PYRAMID_MAX_LEVELS + 1];
  224. for (int k = 0; k < rt.Length; k++) {
  225. rt[k].rtDown = Shader.PropertyToID("_VFogDownMip" + k);
  226. rt[k].rtUp = Shader.PropertyToID("_VFogUpMip" + k);
  227. }
  228. }
  229. int width = GetScaledSize(sourceDesc.width, downscaling);
  230. int height = GetScaledSize(sourceDesc.height, downscaling);
  231. if (downscaling > 1 && manager.scatteringHighQuality) {
  232. mat.EnableKeyword(ShaderParams.SKW_SCATTERING_HQ);
  233. } else {
  234. mat.DisableKeyword(ShaderParams.SKW_SCATTERING_HQ);
  235. }
  236. if (!manager.scatteringHighQuality) {
  237. width /= 2;
  238. height /= 2;
  239. }
  240. int mipCount = manager.scatteringHighQuality ? 5 : 4;
  241. RenderTextureDescriptor scatterDesc = sourceDesc;
  242. scatterDesc.colorFormat = RenderTextureFormat.ARGBHalf;
  243. scatterDesc.msaaSamples = 1;
  244. scatterDesc.depthBufferBits = 0;
  245. for (int k = 0; k <= mipCount; k++) {
  246. if (width < 2) width = 2;
  247. if (height < 2) height = 2;
  248. scatterDesc.width = rt[k].width = width;
  249. scatterDesc.height = rt[k].height = height;
  250. cmd.GetTemporaryRT(rt[k].rtDown, scatterDesc, FilterMode.Bilinear);
  251. cmd.GetTemporaryRT(rt[k].rtUp, scatterDesc, FilterMode.Bilinear);
  252. width /= 2;
  253. height /= 2;
  254. }
  255. RenderTargetIdentifier sourceMip = rt[0].rtDown;
  256. FullScreenBlit(cmd, source, sourceMip, mat, (int)Pass.ScatteringPrefilter);
  257. // Blitting down...
  258. cmd.SetGlobalFloat(ShaderParams.BlurScale, 1f);
  259. for (int k = 1; k <= mipCount; k++) {
  260. FullScreenBlit(cmd, sourceMip, rt[k].rtDown, mat, (int)Pass.Resample);
  261. sourceMip = rt[k].rtDown;
  262. }
  263. // Blitting up...
  264. cmd.SetGlobalFloat(ShaderParams.BlurScale, 1.5f);
  265. for (int k = mipCount; k > 0; k--) {
  266. cmd.SetGlobalTexture(ShaderParams.BlurredTex, rt[k - 1].rtDown);
  267. FullScreenBlit(cmd, sourceMip, rt[k - 1].rtUp, mat, (int)Pass.ResampleAndCombine);
  268. sourceMip = rt[k - 1].rtUp;
  269. }
  270. FullScreenBlit(cmd, sourceMip, source, mat, (int)Pass.ScatteringBlend);
  271. }
  272. void FullScreenBlit(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material, int passIndex) {
  273. destination = new RenderTargetIdentifier(destination, 0, CubemapFace.Unknown, -1);
  274. cmd.SetRenderTarget(destination);
  275. cmd.SetGlobalTexture(ShaderParams.MainTex, source);
  276. cmd.DrawMesh(Tools.fullscreenMesh, Matrix4x4.identity, material, 0, passIndex);
  277. }
  278. public void Cleanup() {
  279. Shader.SetGlobalInt(ShaderParams.ForcedInvisible, 0);
  280. }
  281. }
  282. [SerializeField, HideInInspector]
  283. Shader blurShader;
  284. VolumetricFogRenderPass fogRenderPass;
  285. BlurRenderPass blurRenderPass;
  286. public static bool installed;
  287. public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingTransparents;
  288. [Tooltip("Specify which fog volumes will be rendered by this feature.")]
  289. public LayerMask fogLayerMask = -1;
  290. [Tooltip("Specify which cameras can execute this render feature. If you have several cameras in your scene, make sure only the correct cameras use this feature in order to optimize performance.")]
  291. public LayerMask cameraLayerMask = -1;
  292. [Tooltip("Ignores reflection probes from executing this render feature")]
  293. public bool ignoreReflectionProbes = true;
  294. void OnDisable() {
  295. installed = false;
  296. if (blurRenderPass != null) {
  297. blurRenderPass.Cleanup();
  298. }
  299. }
  300. private void OnDestroy() {
  301. if (fogRenderPass != null) {
  302. fogRenderPass.CleanUp();
  303. }
  304. }
  305. public override void Create() {
  306. name = "Volumetric Fog 2";
  307. fogRenderPass = new VolumetricFogRenderPass();
  308. blurRenderPass = new BlurRenderPass();
  309. blurShader = Shader.Find("Hidden/VolumetricFog2/Blur");
  310. if (blurShader == null) {
  311. Debug.LogWarning("Could not load Volumetric Fog composition shader.");
  312. }
  313. }
  314. // This method is called when setting up the renderer once per-camera.
  315. public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) {
  316. if (VolumetricFog.volumetricFogs.Count == 0) return;
  317. VolumetricFogManager manager = VolumetricFogManager.GetManagerIfExists();
  318. if (manager == null || (manager.downscaling <= 1f && manager.blurPasses < 1 && manager.scattering <= 0)) {
  319. Shader.SetGlobalInt(ShaderParams.ForcedInvisible, 0);
  320. return;
  321. }
  322. Camera cam = renderingData.cameraData.camera;
  323. CameraType camType = cam.cameraType;
  324. if (camType == CameraType.Preview) return;
  325. if (ignoreReflectionProbes && camType == CameraType.Reflection) return;
  326. if ((fogLayerMask & cam.cullingMask) == 0) return;
  327. if ((cameraLayerMask & (1 << cam.gameObject.layer)) == 0) return;
  328. if (cam.targetTexture != null && cam.targetTexture.format == RenderTextureFormat.Depth) return; // ignore occlusion cams!
  329. fogRenderPass.Setup(this);
  330. blurRenderPass.Setup(blurShader, renderer, this);
  331. renderer.EnqueuePass(fogRenderPass);
  332. renderer.EnqueuePass(blurRenderPass);
  333. installed = true;
  334. }
  335. }
  336. }