using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.RenderGraphModule.Util; using UnityEngine.Rendering.Universal; public class UIBlurBehindRendererFeature : ScriptableRendererFeature { public const string GlobalTextureName = "_BansonicUIBlurSourceTex"; private static readonly int GlobalTextureId = Shader.PropertyToID(GlobalTextureName); [Tooltip("When the UI blur background texture should be captured.")] public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing; [Tooltip("Downsample factor for the captured screen texture. Higher values are cheaper and blurrier.")] [Range(1, 4)] public int captureDownsample = 2; private UIBlurBehindPass pass; public override void Create() { if (pass == null) { pass = new UIBlurBehindPass(); } pass.renderPassEvent = renderPassEvent; pass.SetDownsample(captureDownsample); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (pass == null) { return; } Camera camera = renderingData.cameraData.camera; if (camera == null) { return; } CameraType cameraType = renderingData.cameraData.cameraType; if (cameraType == CameraType.Preview || cameraType == CameraType.Reflection) { return; } pass.SetDownsample(captureDownsample); renderer.EnqueuePass(pass); } protected override void Dispose(bool disposing) { if (pass != null) { pass.Dispose(); pass = null; } } private sealed class UIBlurBehindPass : ScriptableRenderPass { private int downsample = 2; private RTHandle blurSourceTexture; public void SetDownsample(int value) { downsample = Mathf.Clamp(value, 1, 4); requiresIntermediateTexture = true; } public void Dispose() { blurSourceTexture?.Release(); blurSourceTexture = null; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { UniversalResourceData resourceData = frameData.Get(); UniversalCameraData cameraData = frameData.Get(); if (resourceData.isActiveTargetBackBuffer) { return; } RenderTextureDescriptor descriptor = cameraData.cameraTargetDescriptor; descriptor.msaaSamples = 1; descriptor.depthBufferBits = 0; descriptor.width = Mathf.Max(1, descriptor.width / downsample); descriptor.height = Mathf.Max(1, descriptor.height / downsample); descriptor.useMipMap = false; descriptor.autoGenerateMips = false; RenderingUtils.ReAllocateHandleIfNeeded( ref blurSourceTexture, descriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: GlobalTextureName); if (blurSourceTexture == null) { return; } Shader.SetGlobalTexture(GlobalTextureId, blurSourceTexture); TextureHandle source = resourceData.activeColorTexture; TextureHandle destination = renderGraph.ImportTexture(blurSourceTexture); if (!source.IsValid() || !destination.IsValid()) { return; } RenderGraphUtils.BlitMaterialParameters parameters = new( source, destination, Blitter.GetBlitMaterial(TextureDimension.Tex2D), 0); renderGraph.AddBlitPass(parameters, "UI Blur Behind Capture Copy"); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor; descriptor.msaaSamples = 1; descriptor.depthBufferBits = 0; descriptor.width = Mathf.Max(1, descriptor.width / downsample); descriptor.height = Mathf.Max(1, descriptor.height / downsample); descriptor.useMipMap = false; descriptor.autoGenerateMips = false; RenderingUtils.ReAllocateHandleIfNeeded( ref blurSourceTexture, descriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: GlobalTextureName); CommandBuffer cmd = CommandBufferPool.Get("UI Blur Behind Capture"); try { if (blurSourceTexture == null) { return; } Blitter.BlitCameraTexture(cmd, renderingData.cameraData.renderer.cameraColorTargetHandle, blurSourceTexture); cmd.SetGlobalTexture(GlobalTextureId, blurSourceTexture.nameID); context.ExecuteCommandBuffer(cmd); } finally { cmd.Clear(); CommandBufferPool.Release(cmd); } } } }