Files
aibis-dream/Assets/Render/KiteFocusSprite.shader
T

237 lines
10 KiB
GLSL

Shader "AIBIS/KiteFocusSprite"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1, 1, 1, 1)
[Header(Focus Blur)]
_BlurAmount ("Blur Amount", Range(0, 1)) = 0
_BlurSize ("Blur Size", Range(0, 0.05)) = 0.012
[HideInInspector] _UseSoftFocus ("Use Soft Focus", Float) = 0
_DefocusBloom ("Defocus Highlight Bloom", Range(0, 1)) = 0.32
_HaloStrength ("Large Circle Halo", Range(0, 0.6)) = 0.22
_ChromaticFringe ("Lens Color Fringe", Range(0, 0.6)) = 0.14
_DefocusVeil ("Defocus White Veil", Range(0, 0.3)) = 0.055
// Required for SpriteMask (same contract as Sprites-Default / UI.Mask)
[HideInInspector][PerRendererData] _StencilComp ("Stencil Comparison", Float) = 8
[HideInInspector][PerRendererData] _Stencil ("Stencil ID", Float) = 0
[HideInInspector][PerRendererData] _StencilOp ("Stencil Operation", Float) = 0
[HideInInspector][PerRendererData] _StencilWriteMask ("Stencil Write Mask", Float) = 255
[HideInInspector][PerRendererData] _StencilReadMask ("Stencil Read Mask", Float) = 255
[HideInInspector] _ColorMask ("Color Mask", Float) = 15
}
SubShader
{
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
ColorMask [_ColorMask]
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
fixed4 _Color;
float _BlurAmount;
float _BlurSize;
float _UseSoftFocus;
float _DefocusBloom;
float _HaloStrength;
float _ChromaticFringe;
float _DefocusVeil;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color * _Color;
o.uv = v.uv;
return o;
}
half4 sampleTex(float2 uv)
{
// 边界外按透明,避免 saturate 夹边造成硬切
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0)
return half4(0, 0, 0, 0);
return tex2D(_MainTex, uv);
}
half4 samplePremultiplied(float2 uv)
{
half4 color = sampleTex(uv);
color.rgb *= color.a;
return color;
}
// 圆形光圈核:比高斯更接近真实镜头的弥散圆,且不会出现十字形拖影。
half4 ApertureDisc(float2 uv, float radius)
{
half4 result = samplePremultiplied(uv) * 0.12;
const half w = 0.044;
// 内圈
result += samplePremultiplied(uv + float2( 0.38, 0.00) * radius) * w;
result += samplePremultiplied(uv + float2(-0.38, 0.00) * radius) * w;
result += samplePremultiplied(uv + float2( 0.00, 0.38) * radius) * w;
result += samplePremultiplied(uv + float2( 0.00, -0.38) * radius) * w;
result += samplePremultiplied(uv + float2( 0.27, 0.27) * radius) * w;
result += samplePremultiplied(uv + float2(-0.27, 0.27) * radius) * w;
result += samplePremultiplied(uv + float2( 0.27, -0.27) * radius) * w;
result += samplePremultiplied(uv + float2(-0.27, -0.27) * radius) * w;
// 外圈使用不规则角度,避免规则采样形成振铃或星芒。
result += samplePremultiplied(uv + float2( 0.98, 0.05) * radius) * w;
result += samplePremultiplied(uv + float2( 0.82, 0.55) * radius) * w;
result += samplePremultiplied(uv + float2( 0.43, 0.90) * radius) * w;
result += samplePremultiplied(uv + float2(-0.16, 0.98) * radius) * w;
result += samplePremultiplied(uv + float2(-0.69, 0.72) * radius) * w;
result += samplePremultiplied(uv + float2(-0.99, 0.10) * radius) * w;
result += samplePremultiplied(uv + float2(-0.80, -0.58) * radius) * w;
result += samplePremultiplied(uv + float2(-0.36, -0.92) * radius) * w;
result += samplePremultiplied(uv + float2( 0.22, -0.97) * radius) * w;
result += samplePremultiplied(uv + float2( 0.72, -0.68) * radius) * w;
result += samplePremultiplied(uv + float2( 0.94, -0.31) * radius) * w;
result += samplePremultiplied(uv + float2(-0.48, 0.86) * radius) * w;
return result;
}
half4 WideHalo(float2 uv, float radius)
{
half4 result = 0;
result += samplePremultiplied(uv + float2( 1.00, 0.00) * radius);
result += samplePremultiplied(uv + float2( 0.71, 0.71) * radius);
result += samplePremultiplied(uv + float2( 0.00, 1.00) * radius);
result += samplePremultiplied(uv + float2(-0.71, 0.71) * radius);
result += samplePremultiplied(uv + float2(-1.00, 0.00) * radius);
result += samplePremultiplied(uv + float2(-0.71, -0.71) * radius);
result += samplePremultiplied(uv + float2( 0.00, -1.00) * radius);
result += samplePremultiplied(uv + float2( 0.71, -0.71) * radius);
return result * 0.125;
}
half4 SoftFocusBlur(float2 uv, float radius)
{
float nearRadius = radius * 0.48;
half4 result = samplePremultiplied(uv) * 0.20;
result += samplePremultiplied(uv + float2( nearRadius, 0)) * 0.08;
result += samplePremultiplied(uv + float2(-nearRadius, 0)) * 0.08;
result += samplePremultiplied(uv + float2(0, nearRadius)) * 0.08;
result += samplePremultiplied(uv + float2(0, -nearRadius)) * 0.08;
result += samplePremultiplied(uv + float2( nearRadius, nearRadius)) * 0.055;
result += samplePremultiplied(uv + float2(-nearRadius, nearRadius)) * 0.055;
result += samplePremultiplied(uv + float2( nearRadius, -nearRadius)) * 0.055;
result += samplePremultiplied(uv + float2(-nearRadius, -nearRadius)) * 0.055;
result += samplePremultiplied(uv + float2( radius, 0)) * 0.04;
result += samplePremultiplied(uv + float2(-radius, 0)) * 0.04;
result += samplePremultiplied(uv + float2(0, radius)) * 0.04;
result += samplePremultiplied(uv + float2(0, -radius)) * 0.04;
result += samplePremultiplied(uv + float2( radius, radius)) * 0.025;
result += samplePremultiplied(uv + float2(-radius, radius)) * 0.025;
result += samplePremultiplied(uv + float2( radius, -radius)) * 0.025;
result += samplePremultiplied(uv + float2(-radius, -radius)) * 0.025;
return result;
}
fixed4 frag(v2f i) : SV_Target
{
float blurAmt = saturate(_BlurAmount);
half4 sharp = sampleTex(i.uv);
if (blurAmt < 0.001)
{
half4 col = sharp * i.color;
col.rgb *= col.a;
return col;
}
float2 texel = max(abs(_MainTex_TexelSize.xy), float2(1e-5, 1e-5));
float radius = (length(texel) * 0.5 + _BlurSize) * (0.28 + blurAmt * 1.72);
if (_UseSoftFocus > 0.5)
{
half4 softBlurred = SoftFocusBlur(i.uv, radius);
half4 softSharp = sharp;
softSharp.rgb *= softSharp.a;
half4 softColor = lerp(softSharp, softBlurred, smoothstep(0.0, 1.0, blurAmt));
softColor.rgb *= i.color.rgb * i.color.a;
softColor.a *= i.color.a;
return softColor;
}
half4 disc = ApertureDisc(i.uv, radius);
half4 halo = WideHalo(i.uv, radius * 1.85);
half4 blurred = lerp(disc, halo, saturate(_HaloStrength * blurAmt));
// 很轻的 RGB 焦平面差异,避免单纯软件模糊的塑料感。
float2 fringe = float2(0.73, 0.41) * radius * _ChromaticFringe * blurAmt;
half4 fringeR = samplePremultiplied(i.uv + fringe);
half4 fringeB = samplePremultiplied(i.uv - fringe);
blurred.r = lerp(blurred.r, fringeR.r, _ChromaticFringe * blurAmt);
blurred.b = lerp(blurred.b, fringeB.b, _ChromaticFringe * blurAmt);
// 参考片中的失焦会让亮部扩散并稍微泛白,但保留在物体 alpha 内。
half luminance = dot(blurred.rgb, half3(0.2126, 0.7152, 0.0722));
half highlight = saturate(luminance - blurred.a * 0.42);
blurred.rgb += highlight * _DefocusBloom * blurAmt;
blurred.rgb += blurred.a * _DefocusVeil * blurAmt;
half4 sharpPremul = sharp;
sharpPremul.rgb *= sharpPremul.a;
float mixT = smoothstep(0.0, 1.0, blurAmt);
half4 col = lerp(sharpPremul, blurred, mixT);
col.rgb *= i.color.rgb * i.color.a;
col.a *= i.color.a;
return col;
}
ENDCG
}
}
}