Files
SoulliesOfficial 50ee502684 完善
2026-02-13 09:22:11 -05:00

140 lines
6.7 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 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 之前效果最强,但放在 AfterPostProcess 最安全(不易过曝)
// 这里建议 AfterPostProcess配合 HDR 使用
public override CustomPostProcessInjectionPoint InjectionPoint => CustomPostProcessInjectionPoint.AfterPostProcess;
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.7f, 0f, 5f); // 推荐 0.5 - 1.0
[Tooltip("迭代次数。次数越多,光晕越平滑、范围越大,但性能开销越高。")]
public ClampedIntParameter diffusion = new(6, 1, 8); // 6次通常足够高品质
[Tooltip("泛光染色。可以做粉色霓虹、蓝色科技光等效果。")]
public ColorParameter tint = new(Color.white, true, true, true);
// 内部使用的 RT 数组
private RTHandle[] _bloomPyramidUp;
private RTHandle[] _bloomPyramidDown;
private const int k_MaxPyramidSize = 16;
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] 上
// 为了保留细节Dual Kawase 通常是将 Up[i+1] 叠加回 Down[i] 存入 Up[i]
// 第一步:先把 Down[i] 拷进 Up[i] 作为底图
Blitter.BlitCameraTexture(cmd, _bloomPyramidDown[i], _bloomPyramidUp[i]);
// 第二步:把 Up[i+1] 升采样并 Additive 混合进 Up[i]
// Shader Pass 2 开启了 Blend One One
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;
}
}