调整Bloom
This commit is contained in:
@@ -9,118 +9,170 @@ Shader "SLS/Postprocessing/AnimeBloom"
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
|
||||
|
||||
// --- 参数定义 ---
|
||||
float4 _BloomParams; // x: Intensity, y: Threshold, z: SoftKnee, w: Clamp
|
||||
float4 _BloomTint; // 泛光染色
|
||||
float _BlurRadius; // 模糊扩散半径 (控制光晕大小的关键)
|
||||
// =====================================================
|
||||
// 参数定义(与 Unity 原生 Bloom.shader 对齐)
|
||||
// =====================================================
|
||||
// _BloomScatterParams:
|
||||
// x: scatter (已经过 C# 侧 Lerp(0.05, 0.95, userValue) 映射)
|
||||
// y: clamp (最大亮度限制,防萤火虫)
|
||||
// z: threshold (线性空间,C# 侧已做 GammaToLinear)
|
||||
// w: thresholdKnee (= threshold * 0.5f,硬编码 soft knee)
|
||||
float4 _BloomScatterParams;
|
||||
#define Scatter _BloomScatterParams.x
|
||||
#define ClampMax _BloomScatterParams.y
|
||||
#define Threshold _BloomScatterParams.z
|
||||
#define ThresholdKnee _BloomScatterParams.w
|
||||
|
||||
// 纹理
|
||||
// Composite 阶段参数:x=intensity, y/z/w=tint.rgb (已归一化亮度)
|
||||
float4 _BloomParams;
|
||||
float4 _BloomTint; // 保留兼容,不再使用
|
||||
|
||||
float _KernelScale; // 采样跨度放大乘数,默认 1.0
|
||||
|
||||
// SourceTexLowMip: upsample 时的"低频大光晕"纹理 (lowMip)
|
||||
TEXTURE2D(_SourceTexLowMip);
|
||||
SAMPLER(sampler_SourceTexLowMip);
|
||||
|
||||
// 最终 bloom 结果纹理(传入 Composite Pass)
|
||||
TEXTURE2D(_BloomTex);
|
||||
SAMPLER(sampler_BloomTex);
|
||||
|
||||
// --- 辅助函数:Prefilter (提取高亮) ---
|
||||
half3 Prefilter(half3 color)
|
||||
// =====================================================
|
||||
// HDR 编解码(与 Unity 原生 Bloom.shader 完全一致)
|
||||
// 在线性工作流下 encode/decode 是 no-op,但写清楚以防 gamma 空间项目
|
||||
// =====================================================
|
||||
half4 EncodeHDR(half3 color)
|
||||
{
|
||||
float threshold = _BloomParams.y;
|
||||
float softKnee = _BloomParams.z;
|
||||
float clampVal = _BloomParams.w;
|
||||
|
||||
// 1. 限制最大亮度 (防闪烁/萤火虫噪点)
|
||||
color = min(color, clampVal);
|
||||
|
||||
// 2. 阈值计算 (使用 Soft Knee 曲线让过渡更自然)
|
||||
// 标准公式:(Brightness - Threshold) / max(Brightness, 0.0001)
|
||||
// 这里使用一个更平滑的曲线版本,防止高光边缘切变太硬
|
||||
float brightness = Max3(color.r, color.g, color.b);
|
||||
float soft = brightness - threshold + softKnee;
|
||||
soft = clamp(soft, 0, 2 * softKnee);
|
||||
soft = soft * soft / (4 * softKnee + 1e-4);
|
||||
|
||||
float contribution = max(soft, brightness - threshold);
|
||||
contribution /= max(brightness, 1e-4);
|
||||
|
||||
return color * contribution;
|
||||
}
|
||||
|
||||
// --- Pass 0: Prefilter ---
|
||||
half4 FragPrefilter(Varyings input) : SV_Target
|
||||
{
|
||||
// 采样原图
|
||||
half4 color = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, input.texcoord);
|
||||
// 提取高亮
|
||||
half3 bloom = Prefilter(color.rgb);
|
||||
return half4(bloom, 1.0);
|
||||
}
|
||||
|
||||
// --- Pass 1: Downsample (Kawase 4-Tap) ---
|
||||
// 降采样:取 4 个对角像素的平均值,范围随分辨率降低而扩大
|
||||
half4 FragDownsample(Varyings input) : SV_Target
|
||||
{
|
||||
float2 uv = input.texcoord;
|
||||
float4 texelSize = _BlitTexture_TexelSize;
|
||||
|
||||
// 原生的 Kawase Offset 是 1.0(即 0.5 个对角像素距离),
|
||||
// 这里加入 _BlurRadius 按比例扩大步幅,能够使用 3 次迭代跑出原版 6 次的扩散面积
|
||||
float spreadOffset = 1.0 + _BlurRadius * 0.5;
|
||||
float2 offset = texelSize.xy * spreadOffset;
|
||||
|
||||
half3 c0 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv - offset).rgb;
|
||||
half3 c1 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2(offset.x, -offset.y)).rgb;
|
||||
half3 c2 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv - float2(offset.x, -offset.y)).rgb;
|
||||
half3 c3 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + offset).rgb;
|
||||
|
||||
half3 color = (c0 + c1 + c2 + c3) * 0.25;
|
||||
#if UNITY_COLORSPACE_GAMMA
|
||||
color = sqrt(color);
|
||||
#endif
|
||||
return half4(color, 1.0);
|
||||
}
|
||||
|
||||
TEXTURE2D(_BloomMipDown);
|
||||
SAMPLER(sampler_BloomMipDown);
|
||||
|
||||
// --- Pass 2: Upsample (Kawase 4-Tap + Scatter) ---
|
||||
half4 FragUpsample(Varyings input) : SV_Target
|
||||
half3 DecodeHDR(half4 data)
|
||||
{
|
||||
float2 uv = input.texcoord;
|
||||
float4 texelSize = _BlitTexture_TexelSize;
|
||||
|
||||
// 原生的 Kawase 升采样偏移是 0.5。
|
||||
// 配合宽幅降采样,这里稍微放大一点点就可以获得非常柔顺的大面积泛光
|
||||
float spreadOffset = 0.5 + _BlurRadius * 0.2;
|
||||
float2 offset = texelSize.xy * spreadOffset;
|
||||
|
||||
// 4-Tap 从更低分辨率纹理 (Up[i+1]) 采样外围光晕
|
||||
half3 c0 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv - offset * float2(1, 1)).rgb;
|
||||
half3 c1 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + offset * float2(1, -1)).rgb;
|
||||
half3 c2 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv - offset * float2(1, -1)).rgb;
|
||||
half3 c3 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + offset * float2(1, 1)).rgb;
|
||||
half3 lowRes = (c0 + c1 + c2 + c3) * 0.25;
|
||||
|
||||
// 采样当前层级的高清纹理 (Down[i])
|
||||
half3 highRes = SAMPLE_TEXTURE2D_X(_BloomMipDown, sampler_LinearClamp, uv).rgb;
|
||||
|
||||
// 【完全复刻 Unity 原生逻辑】: highRes + lowRes * scatter
|
||||
// 这既保证了光晕向外漫射(低迭代扩散广),又保证了核心亮区的能量守恒!
|
||||
float scatter = saturate(_BlurRadius);
|
||||
half3 bloom = highRes + lowRes * scatter;
|
||||
|
||||
return half4(bloom, 1.0);
|
||||
half3 color = data.xyz;
|
||||
#if UNITY_COLORSPACE_GAMMA
|
||||
color *= color;
|
||||
#endif
|
||||
return color;
|
||||
}
|
||||
|
||||
// --- Pass 3: Composite (最终合成) ---
|
||||
// =====================================================
|
||||
// Pass 0: Prefilter — 提取超过阈值的高亮像素
|
||||
// 与原生完全一致的 soft knee 公式
|
||||
// =====================================================
|
||||
half4 FragPrefilter(Varyings input) : SV_Target
|
||||
{
|
||||
float2 uv = input.texcoord;
|
||||
half3 color = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv));
|
||||
|
||||
// 1. 亮度钳制(防止极亮萤火虫像素跳变)
|
||||
color = min(color, ClampMax);
|
||||
|
||||
// 2. Soft Knee 阈值(与 Unity 原生公式完全一致)
|
||||
half brightness = Max3(color.r, color.g, color.b);
|
||||
half softness = clamp(brightness - Threshold + ThresholdKnee, 0.0, 2.0 * ThresholdKnee);
|
||||
softness = (softness * softness) / (4.0 * ThresholdKnee + 1e-4);
|
||||
half multiplier = max(brightness - Threshold, softness) / max(brightness, 1e-4);
|
||||
color *= multiplier;
|
||||
|
||||
// 3. 防止 NaN 传播(负值在 EncodeHDR sqrt 时会产生 NaN)
|
||||
color = max(color, 0);
|
||||
|
||||
return EncodeHDR(color);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// Pass 1: Dual Kawase Downsample
|
||||
// 公式:1/8 * (center*4 + 4corners)
|
||||
// 采样次数:5次(相当于 3×3 box + 双线性权重,效果非常柔和)
|
||||
// 与 Unity 原生 Bloom.shader FragDualDownsample 完全一致
|
||||
// =====================================================
|
||||
half4 FragDualDownsample(Varyings input) : SV_Target
|
||||
{
|
||||
float2 uv = input.texcoord;
|
||||
// 乘以 _KernelScale,在极低迭代次数下强制向外大跨步拉扯光晕
|
||||
float2 ts = _BlitTexture_TexelSize.xy * _KernelScale;
|
||||
|
||||
// 中心点(权重 4)
|
||||
half3 c0 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv));
|
||||
|
||||
// 4 个对角偏移各 0.5 像素(恰好落在 4 像素的双线性插值中心)
|
||||
half3 c1 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2( 0.5, 0.5) * ts));
|
||||
half3 c2 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2(-0.5, 0.5) * ts));
|
||||
half3 c3 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2(-0.5, -0.5) * ts));
|
||||
half3 c4 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2( 0.5, -0.5) * ts));
|
||||
|
||||
// 加权平均:(c0*4 + c1+c2+c3+c4) / 8
|
||||
half3 color = (1.0 / 8.0) * (c0 * 4.0 + c1 + c2 + c3 + c4);
|
||||
return EncodeHDR(color);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// Pass 2: Dual Kawase Upsample + Energy-Conserving Lerp
|
||||
//
|
||||
// 关键核心:与 Unity 原生 FragUpsample 一致的混合公式
|
||||
// result = lerp(highMip, lowMip, Scatter)
|
||||
//
|
||||
// highMip (_BlitTexture) = 当前层的降采样纹理 Down[i](高分辨率,细节)
|
||||
// lowMip (_SourceTexLowMip) = 上一层的升采样结果 Up[i+1](低分辨率,扩散光晕)
|
||||
//
|
||||
// lerp 是能量守恒的:总亮度 = (1-s)*high + s*low,永远 ≤ max(high,low)
|
||||
// 这是防止"死白"的数学保证。
|
||||
// =====================================================
|
||||
half4 FragDualUpsample(Varyings input) : SV_Target
|
||||
{
|
||||
float2 uv = input.texcoord;
|
||||
// 同样乘以 _KernelScale
|
||||
float2 ts = _BlitTexture_TexelSize.xy * _KernelScale;
|
||||
|
||||
// Dual Kawase 8-tap 升采样:4 对角 + 4 正交
|
||||
// 正交距离 = 1.0 texel,对角距离 = 0.5 texel(与原生完全一致)
|
||||
half3 c1 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2( 0.5, 0.5) * ts));
|
||||
half3 c2 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2(-0.5, 0.5) * ts));
|
||||
half3 c3 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2(-0.5, -0.5) * ts));
|
||||
half3 c4 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2( 0.5, -0.5) * ts));
|
||||
|
||||
half3 c5 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2(-1.0, 0.0) * ts));
|
||||
half3 c6 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2( 1.0, 0.0) * ts));
|
||||
half3 c7 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2( 0.0, 1.0) * ts));
|
||||
half3 c8 = DecodeHDR(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + float2( 0.0, -1.0) * ts));
|
||||
|
||||
// 加权平均 (4角×2 + 4正交×1) / 12,产生柔和的钟形分布
|
||||
half3 highMip = (1.0 / 12.0) * ((c1 + c2 + c3 + c4) * 2.0 + c5 + c6 + c7 + c8);
|
||||
|
||||
// lowMip = 来自更低分辨率层(Up[i+1])的扩散光晕,双线性采样
|
||||
half3 lowMip = DecodeHDR(SAMPLE_TEXTURE2D(_SourceTexLowMip, sampler_LinearClamp, uv));
|
||||
|
||||
// 【核心公式,与原生完全一致】
|
||||
// Scatter 控制光晕扩散程度:0=只有当前层细节,1=完全使用模糊层
|
||||
// 始终能量守恒,绝对不会产生死白
|
||||
half3 result = lerp(highMip, lowMip, Scatter);
|
||||
return EncodeHDR(result);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// Pass 3: Composite — 将最终 bloom 叠加回原始画面
|
||||
// _BloomParams.x = intensity
|
||||
// _BloomParams.yzw = tint.rgb (亮度已归一化,仅携带色相/饱和度)
|
||||
// =====================================================
|
||||
half4 FragComposite(Varyings input) : SV_Target
|
||||
{
|
||||
float2 uv = input.texcoord;
|
||||
|
||||
// 原始画面
|
||||
|
||||
// 原始 HDR 画面
|
||||
half4 baseColor = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv);
|
||||
|
||||
// 泛光结果 (经过多次升采样后的最终纹理)
|
||||
half3 bloom = SAMPLE_TEXTURE2D(_BloomTex, sampler_BloomTex, uv).rgb;
|
||||
|
||||
// 应用强度和染色
|
||||
bloom *= _BloomParams.x * _BloomTint.rgb;
|
||||
// Bloom 纹理(已经过多轮 Dual Kawase 升降采样)
|
||||
half3 bloom = DecodeHDR(SAMPLE_TEXTURE2D(_BloomTex, sampler_BloomTex, uv));
|
||||
|
||||
// 叠加 (Additive)
|
||||
// 也可以尝试 Screen 混合模式让光变得更柔和,但 Additive 最符合物理发光
|
||||
// 应用强度和染色(_BloomParams.yzw 是亮度归一化的 tint)
|
||||
float intensity = _BloomParams.x;
|
||||
half3 tint = _BloomParams.yzw;
|
||||
bloom *= intensity * tint;
|
||||
|
||||
// Additive 合并(物理正确的发光叠加)
|
||||
return half4(baseColor.rgb + bloom, baseColor.a);
|
||||
}
|
||||
|
||||
@@ -129,9 +181,9 @@ Shader "SLS/Postprocessing/AnimeBloom"
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
|
||||
ZWrite Off Cull Off
|
||||
ZWrite Off Cull Off ZTest Always
|
||||
|
||||
// 0: Prefilter
|
||||
// Pass 0: Prefilter
|
||||
Pass
|
||||
{
|
||||
Name "Bloom Prefilter"
|
||||
@@ -141,28 +193,27 @@ Shader "SLS/Postprocessing/AnimeBloom"
|
||||
ENDHLSL
|
||||
}
|
||||
|
||||
// 1: Downsample
|
||||
// Pass 1: Dual Kawase Downsample
|
||||
Pass
|
||||
{
|
||||
Name "Bloom Downsample"
|
||||
Name "Bloom Dual Downsample"
|
||||
HLSLPROGRAM
|
||||
#pragma vertex Vert
|
||||
#pragma fragment FragDownsample
|
||||
#pragma fragment FragDualDownsample
|
||||
ENDHLSL
|
||||
}
|
||||
|
||||
// 2: Upsample
|
||||
// Pass 2: Dual Kawase Upsample + lerp(high, low, scatter)
|
||||
Pass
|
||||
{
|
||||
Name "Bloom Upsample"
|
||||
// 已在内部 Lerp,无需外部 Additive Blend
|
||||
Name "Bloom Dual Upsample"
|
||||
HLSLPROGRAM
|
||||
#pragma vertex Vert
|
||||
#pragma fragment FragUpsample
|
||||
#pragma fragment FragDualUpsample
|
||||
ENDHLSL
|
||||
}
|
||||
|
||||
// 3: Composite
|
||||
// Pass 3: Composite
|
||||
Pass
|
||||
{
|
||||
Name "Bloom Composite"
|
||||
|
||||
Reference in New Issue
Block a user