提交毛玻璃测试

This commit is contained in:
Bob.Song
2026-02-03 17:47:50 +08:00
parent da95507d5a
commit 10c6fabd73
16 changed files with 923 additions and 2 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bedf4887d50d409fb132fee064bc8b37
timeCreated: 1770108259

View File

@@ -0,0 +1,25 @@
using UnityEngine;
using UnityEngine.UI;
namespace NBF.Blur
{
public class TestBlur : MonoBehaviour
{
public RawImage raw;
public void Test()
{
Shader.SetGlobalInt("_UIBlurRefresh", 1);
// var blur = URPRendererFeatureUtil.GetRendererFeature<UIBlurDualKawaseFeature>();
// blur?.RefreshOnce();
}
void LateUpdate()
{
if (!raw) return;
raw.texture = Shader.GetGlobalTexture("_UIBlurTexture");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d6c5972785cf41e997b1bd6164a04e00
timeCreated: 1770108402

View File

@@ -0,0 +1,179 @@
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);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d992ccf80d34101a9e62b066d753dc3
timeCreated: 1770108262