using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering.RenderGraphModule; public class BlurRendererFeature : ScriptableRendererFeature { [System.Serializable] public class Settings { [Range(0f, 0.2f)] public float blurRadius = 0.02f; [Range(0f, 1f)] public float glowThreshold = 0.6f; [Range(0f, 2f)] public float glowIntensity = 0f; public Material blurHorizontal; public Material blurVertical; } public Settings settings = new Settings(); BlurPass horizontalPass; BlurPass verticalPass; CopyToRTPass copyPass; RenderTexture blurRT; RTHandle blurRTHandle; static readonly int BlurTexID = Shader.PropertyToID("_BlurredTexture"); static readonly int BlurRadiusID = Shader.PropertyToID("_BlurRadius"); static readonly int GlowThresholdID = Shader.PropertyToID("_GlowThreshold"); static readonly int GlowIntensityID = Shader.PropertyToID("_GlowIntensity"); static readonly int BlurSourceID = Shader.PropertyToID("_BlurSource"); public override void Create() { horizontalPass = new BlurPass("Blur Horizontal", RenderPassEvent.BeforeRenderingPostProcessing); verticalPass = new BlurPass("Blur Vertical", RenderPassEvent.BeforeRenderingPostProcessing); copyPass = new CopyToRTPass(RenderPassEvent.BeforeRenderingPostProcessing); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (settings.blurHorizontal == null || settings.blurVertical == null) return; var desc = renderingData.cameraData.cameraTargetDescriptor; if (blurRT != null && (blurRT.width != desc.width / 2 || blurRT.height != desc.height / 2)) { blurRT.Release(); blurRTHandle?.Release(); blurRT = null; blurRTHandle = null; } if (blurRT == null) { blurRT = new RenderTexture(desc.width / 4, desc.height / 4, 0, RenderTextureFormat.DefaultHDR); blurRT.Create(); blurRTHandle = RTHandles.Alloc(blurRT); Shader.SetGlobalTexture(BlurTexID, blurRT); } Shader.SetGlobalFloat(BlurRadiusID, settings.blurRadius); Shader.SetGlobalFloat(GlowThresholdID, settings.glowThreshold); Shader.SetGlobalFloat(GlowIntensityID, settings.glowIntensity); horizontalPass.Setup(settings.blurHorizontal); renderer.EnqueuePass(horizontalPass); verticalPass.Setup(settings.blurVertical); renderer.EnqueuePass(verticalPass); copyPass.Setup(blurRTHandle); renderer.EnqueuePass(copyPass); } protected override void Dispose(bool disposing) { if (blurRT != null) { blurRT.Release(); blurRT = null; } blurRTHandle?.Release(); blurRTHandle = null; } class BlurPass : ScriptableRenderPass { string passName; Material material; static readonly int BlurSourceID = Shader.PropertyToID("_BlurSource"); public BlurPass(string name, RenderPassEvent evt) { passName = name; renderPassEvent = evt; } public void Setup(Material mat) => material = mat; class PassData { public TextureHandle source; public Material material; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { var resourceData = frameData.Get(); var srcDesc = renderGraph.GetTextureDesc(resourceData.activeColorTexture); srcDesc.name = passName + " Temp"; srcDesc.clearBuffer = false; var tempHandle = renderGraph.CreateTexture(srcDesc); using (var builder = renderGraph.AddRasterRenderPass(passName + " Blit", out var passData)) { passData.source = resourceData.activeColorTexture; passData.material = material; builder.UseTexture(passData.source, AccessFlags.Read); builder.SetRenderAttachment(tempHandle, 0, AccessFlags.Write); builder.AllowGlobalStateModification(true); builder.SetRenderFunc((PassData data, RasterGraphContext ctx) => { ctx.cmd.SetGlobalTexture(BlurSourceID, data.source); Blitter.BlitTexture(ctx.cmd, data.source, new Vector4(1, 1, 0, 0), data.material, 0); }); } using (var builder = renderGraph.AddRasterRenderPass(passName + " Restore", out var passData)) { passData.source = tempHandle; builder.UseTexture(passData.source, AccessFlags.Read); builder.SetRenderAttachment(resourceData.activeColorTexture, 0, AccessFlags.Write); builder.SetRenderFunc((PassData data, RasterGraphContext ctx) => { Blitter.BlitTexture(ctx.cmd, data.source, new Vector4(1, 1, 0, 0), 0, false); }); } } } class CopyToRTPass : ScriptableRenderPass { RTHandle targetHandle; public CopyToRTPass(RenderPassEvent evt) => renderPassEvent = evt; public void Setup(RTHandle handle) => targetHandle = handle; class CopyPassData { public TextureHandle source; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { var resourceData = frameData.Get(); var destHandle = renderGraph.ImportTexture(targetHandle); using (var builder = renderGraph.AddRasterRenderPass("Copy Blur To RT", out var passData)) { passData.source = resourceData.activeColorTexture; builder.UseTexture(passData.source, AccessFlags.Read); builder.SetRenderAttachment(destHandle, 0, AccessFlags.Write); builder.SetRenderFunc((CopyPassData data, RasterGraphContext ctx) => { Blitter.BlitTexture(ctx.cmd, data.source, new Vector4(1, 1, 0, 0), 0, false); }); } } } }