using Echovoid.Runtime.Behavior.Rendering; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; namespace SLSUtilities.Rendering.PostProcessing { [System.Serializable, VolumeComponentMenu("SLS/Postprocessing/Anime Bloom")] public class AnimeBloom : ScriptablePostProcessorVolume { // 【核心修改】:放在 ToneMapping 之前执行(BeforePostProcess),配合 ACES 才能彻底解决“高光泛白”问题 public override CustomPostProcessInjectionPoint InjectionPoint => CustomPostProcessInjectionPoint.BeforePostProcess; public override int OrderInInjectionPoint => 5; // 放在 Vignette 之前 [Header("Glow Settings")] [Tooltip("泛光强度。值越大越亮。")] public ClampedFloatParameter intensity = new(0f, 0f, 10f); [Tooltip("阈值。亮度超过此值的像素才会发光。\n关键:设为 1.1 可以过滤掉白墙(1.0),只让灯光发光。")] public MinFloatParameter threshold = new(1.1f, 0f); // 默认设为 1.1 [Tooltip("柔膝 (Soft Knee)。让阈值过渡更平滑,避免高光边缘有硬切痕迹。")] public ClampedFloatParameter softKnee = new(0.5f, 0f, 1f); [Tooltip("最大亮度钳制。防止极亮像素(如太阳)产生乱跳的噪点。")] public MinFloatParameter clamp = new(65472f, 1f); // 默认很大,基本不限制 [Header("Anime Style")] [Tooltip("扩散半径。稍微增加扩展范围来弥补低迭代带来的发光不足。")] public ClampedFloatParameter scatter = new(0.85f, 0f, 5f); // 推荐增量以适应低迭代 [Tooltip("迭代次数。针对移动端带宽优化,建议控制在 2~3 次。")] public ClampedIntParameter diffusion = new(3, 1, 4); // 移动端性能优化,最高4次 [Tooltip("泛光染色。可以做粉色霓虹、蓝色科技光等效果。")] public ColorParameter tint = new(Color.white, true, true, true); // 内部使用的 RT 数组 private RTHandle[] _bloomPyramidUp; private RTHandle[] _bloomPyramidDown; private const int k_MaxPyramidSize = 6; // 减少不必要的最大数组长度 public override string GetShaderName() => "SLS/Postprocessing/AnimeBloom"; public override void Render(CommandBuffer cmd, ref RenderingData renderingData, RTHandle source, RTHandle destination) { if (material == null) return; var desc = renderingData.cameraData.cameraTargetDescriptor; desc.msaaSamples = 1; desc.depthBufferBits = 0; // 1. 设置参数 Vector4 bloomParams = new Vector4(intensity.value, threshold.value, softKnee.value, clamp.value); material.SetVector(InternalShaderHelpers.ID._BloomParams, bloomParams); material.SetVector(InternalShaderHelpers.ID._BloomTint, tint.value); material.SetFloat(InternalShaderHelpers.ID._BlurRadius, scatter.value); // 2. 初始化金字塔数组 int iterations = Mathf.Clamp(diffusion.value, 1, k_MaxPyramidSize); // 确保 RT 数组大小足够 if (_bloomPyramidUp == null || _bloomPyramidUp.Length != k_MaxPyramidSize) { _bloomPyramidUp = new RTHandle[k_MaxPyramidSize]; _bloomPyramidDown = new RTHandle[k_MaxPyramidSize]; } // 3. Prefilter Pass (提取高亮) // 先降一半分辨率,节省性能且增加模糊感 desc.width = Mathf.Max(1, desc.width >> 1); desc.height = Mathf.Max(1, desc.height >> 1); RenderingUtils.ReAllocateIfNeeded(ref _bloomPyramidDown[0], desc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_BloomMipDown0"); RenderingUtils.ReAllocateIfNeeded(ref _bloomPyramidUp[0], desc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_BloomMipUp0"); // Source -> Down[0] (Prefilter) Blitter.BlitCameraTexture(cmd, source, _bloomPyramidDown[0], material, 0); // 4. Downsample Loop (降采样金字塔) int lastDown = 0; for (int i = 1; i < iterations; i++) { // 每次分辨率减半 desc.width = Mathf.Max(1, desc.width >> 1); desc.height = Mathf.Max(1, desc.height >> 1); RenderingUtils.ReAllocateIfNeeded(ref _bloomPyramidDown[i], desc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_BloomMipDown" + i); RenderingUtils.ReAllocateIfNeeded(ref _bloomPyramidUp[i], desc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_BloomMipUp" + i); // Down[i-1] -> Down[i] Blitter.BlitCameraTexture(cmd, _bloomPyramidDown[i - 1], _bloomPyramidDown[i], material, 1); lastDown = i; } // 5. Upsample Loop (升采样并混合) // 从最小的一张开始,往上叠加 // 先把最小的 Down 直接拷给 Up Blitter.BlitCameraTexture(cmd, _bloomPyramidDown[lastDown], _bloomPyramidUp[lastDown]); for (int i = lastDown - 1; i >= 0; i--) { // 设置上一级 Up 为输入 // Upsample Pass 会混合:Up[i+1] (Blur) + Down[i] (High Res Detail) // 这里我们稍微简化逻辑:直接把 Up[i+1] 升采样并叠加到 Up[i] 上 // 第二步:把 Up[i+1] (LowRes) 和 Down[i] (HighRes) 一起传给 Shader // 在 Shader 内部执行 Lerp 能量守恒融合,彻底替代会导致死白的 Additive 叠加模式 material.SetTexture("_BloomMipDown", _bloomPyramidDown[i]); Blitter.BlitCameraTexture(cmd, _bloomPyramidUp[i + 1], _bloomPyramidUp[i], material, 2); } // 6. Composite (合成) // 此时 _bloomPyramidUp[0] 包含了最终的泛光纹理 material.SetTexture(InternalShaderHelpers.ID._BloomTex, _bloomPyramidUp[0]); // Source + BloomTex -> Destination Blitter.BlitCameraTexture(cmd, source, destination, material, 3); } // 清理 RT public void Dispose() { if (_bloomPyramidDown != null) { for (int i = 0; i < _bloomPyramidDown.Length; i++) { if (_bloomPyramidDown[i] != null) _bloomPyramidDown[i].Release(); if (_bloomPyramidUp[i] != null) _bloomPyramidUp[i].Release(); } } } public override bool IsActive() => intensity.value > 0f; } }