Files
Fishing2/Assets/Scripts/Common/Blur/UIBlurDualKawaseFeature.cs
2026-02-03 17:47:50 +08:00

180 lines
6.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util;
public class UIBlurDualKawaseFeature : ScriptableRendererFeature
{
[System.Serializable]
public class Settings
{
public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
[Range(0.25f, 1f)] public float downscale = 0.5f; // 0.5(中高端) / 0.25(低端更稳)
[Range(1, 6)] public int blurPasses = 4; // 3~4 移动端常用
[Range(0.5f, 4f)] public float offset = 1.0f; // 糊的“扩散感”
public bool updateEveryFrame = false; // 推荐 false只在需要时刷新
public string refreshFlagName = "_UIBlurRefresh"; // UI 触发用
public string globalTextureName = "_UIBlurTexture";
public Shader blurShader; // Hidden/URP/DualKawaseBlur
}
public Settings settings = new Settings();
class Pass : ScriptableRenderPass
{
readonly Settings s;
readonly Material mat;
// 持久 RT让 UI 能跨帧拿到(需要 ImportTexture:contentReference[oaicite:2]{index=2}
RTHandle persistentOutput;
static readonly int SourceTexId = Shader.PropertyToID("_SourceTex");
static readonly int OffsetId = Shader.PropertyToID("_Offset");
public Pass(Settings settings, Material material)
{
s = settings;
mat = material;
}
public RTHandle OutputRT => persistentOutput;
public void EnsureOutput(ref RenderTextureDescriptor desc, int w, int h)
{
desc.width = w;
desc.height = h;
desc.depthBufferBits = 0;
desc.msaaSamples = 1;
desc.useMipMap = false;
desc.autoGenerateMips = false;
RenderingUtils.ReAllocateIfNeeded(
ref persistentOutput,
desc,
FilterMode.Bilinear,
TextureWrapMode.Clamp,
name: s.globalTextureName
);
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
// 资源/相机数据(官方示例也是这么取 activeColorTexture:contentReference[oaicite:3]{index=3}
var resourceData = frameData.Get<UniversalResourceData>();
var cameraData = frameData.Get<UniversalCameraData>();
// 避免从 backbuffer blit官方示例也这么做:contentReference[oaicite:4]{index=4}
if (resourceData.isActiveTargetBackBuffer)
return;
// 刷新策略:不每帧更新时,只有收到 UI 请求才做一轮
if (!s.updateEveryFrame)
{
if (Shader.GetGlobalInt(s.refreshFlagName) == 0)
return;
Shader.SetGlobalInt(s.refreshFlagName, 0);
}
// 基础描述符(跟相机一致)
var desc = cameraData.cameraTargetDescriptor;
desc.depthBufferBits = 0;
desc.msaaSamples = 1;
int baseW = Mathf.Max(16, Mathf.RoundToInt(desc.width * s.downscale));
int baseH = Mathf.Max(16, Mathf.RoundToInt(desc.height * s.downscale));
EnsureOutput(ref desc, baseW, baseH);
// 把持久 RTHandle 导入 RenderGraph跨帧可用:contentReference[oaicite:5]{index=5}
TextureHandle outTex = renderGraph.ImportTexture(persistentOutput);
// 生成金字塔0层(base), 1层(/2), 2层(/4)...
int n = Mathf.Clamp(s.blurPasses, 1, 8);
TextureHandle[] pyr = new TextureHandle[n];
for (int i = 0; i < n; i++)
{
int w = Mathf.Max(8, baseW >> i);
int h = Mathf.Max(8, baseH >> i);
var d = desc;
d.width = w;
d.height = h;
pyr[i] = UniversalRenderer.CreateRenderGraphTexture(renderGraph, d, $"_UIBlurPyr{i}", false);
}
mat.SetFloat(OffsetId, s.offset);
// source相机颜色
TextureHandle src = resourceData.activeColorTexture;
// Downsample chain (pass 0)
{
var p = new RenderGraphUtils.BlitMaterialParameters(src, pyr[0], mat, 0);
renderGraph.AddBlitPass(p, "UIBlur DualKawase Down 0");
}
for (int i = 1; i < n; i++)
{
var p = new RenderGraphUtils.BlitMaterialParameters(pyr[i - 1], pyr[i], mat, 0);
renderGraph.AddBlitPass(p, $"UIBlur DualKawase Down {i}");
}
// Upsample chain (pass 1)
for (int i = n - 1; i > 0; i--)
{
var p = new RenderGraphUtils.BlitMaterialParameters(pyr[i], pyr[i - 1], mat, 1);
renderGraph.AddBlitPass(p, $"UIBlur DualKawase Up {i}");
}
// 最终写到持久输出 RT
{
var p = new RenderGraphUtils.BlitMaterialParameters(pyr[0], outTex, mat, 1);
renderGraph.AddBlitPass(p, "UIBlur DualKawase Final");
}
}
public void Cleanup()
{
persistentOutput?.Release();
persistentOutput = null;
}
}
Material _mat;
Pass _pass;
public override void Create()
{
if (settings.blurShader == null)
settings.blurShader = Shader.Find("Hidden/URP/DualKawaseBlur");
if (settings.blurShader != null)
_mat = CoreUtils.CreateEngineMaterial(settings.blurShader);
_pass = new Pass(settings, _mat) { renderPassEvent = settings.passEvent };
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (_mat == null) return;
if (renderingData.cameraData.cameraType != CameraType.Game) return;
// 这里提前把全局纹理指向持久 RT即使本帧不刷新也能用旧结果
if (_pass.OutputRT != null)
Shader.SetGlobalTexture(settings.globalTextureName, _pass.OutputRT);
renderer.EnqueuePass(_pass);
}
protected override void Dispose(bool disposing)
{
_pass?.Cleanup();
CoreUtils.Destroy(_mat);
}
}