191 lines
7.4 KiB
Plaintext
191 lines
7.4 KiB
Plaintext
Shader "Custom/2D/OutlineShape"
|
||
{
|
||
Properties
|
||
{
|
||
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
|
||
|
||
[Header(Base Settings)]
|
||
[MainColor] [HDR] _BaseColor ("Base Color (本身颜色)", Color) = (1,1,1,1)
|
||
[Range(0, 1)] _BaseAlpha ("Base Alpha (本体透明度)", Float) = 1.0
|
||
|
||
[Space(10)]
|
||
[Header(Outline Settings)]
|
||
[Toggle(_OUTLINE_ON)] _EnableOutline("Enable Outline (启用描边)", Float) = 1
|
||
[HDR] _OutlineColor ("Outline Color (描边颜色)", Color) = (1, 1, 0, 1)
|
||
[Range(0, 50)] _OutlineWidth ("Outline Width (描边粗细 像素)", Float) = 5.0
|
||
[Range(0.01, 1)] _AlphaThreshold ("Inner Threshold (内描边判定阈值:寻找边界)", Float) = 0.5
|
||
[Range(0.01, 1)] _OutlineSmoothness ("Outline Smoothness (描边平滑度:抗锯齿)", Float) = 0.2
|
||
|
||
// Below properties are required for SpriteRenderer internal data passing and ETC1 support
|
||
[HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
|
||
[HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
|
||
[HideInInspector] _AlphaTex ("External Alpha", 2D) = "white" {}
|
||
[HideInInspector] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
|
||
}
|
||
|
||
SubShader
|
||
{
|
||
Tags
|
||
{
|
||
"RenderPipeline" = "UniversalPipeline"
|
||
"Queue" = "Transparent"
|
||
"RenderType" = "Transparent"
|
||
"IgnoreProjector" = "True"
|
||
"PreviewType" = "Plane"
|
||
"CanUseSpriteAtlas" = "True"
|
||
}
|
||
|
||
Cull Off
|
||
Lighting Off
|
||
ZWrite Off
|
||
Blend SrcAlpha OneMinusSrcAlpha
|
||
|
||
Pass
|
||
{
|
||
Name "SpriteOutline"
|
||
|
||
HLSLPROGRAM
|
||
#pragma target 2.0
|
||
#pragma vertex vert
|
||
#pragma fragment frag
|
||
#pragma multi_compile_local _ ETC1_EXTERNAL_ALPHA
|
||
// 新增: 基于本地关键字的着色器变体,如果关闭描边,将彻底剔除描边代码
|
||
#pragma shader_feature_local_fragment _OUTLINE_ON
|
||
|
||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||
|
||
struct Attributes
|
||
{
|
||
float3 positionOS : POSITION;
|
||
float4 color : COLOR;
|
||
float2 uv : TEXCOORD0;
|
||
};
|
||
|
||
struct Varyings
|
||
{
|
||
float4 positionCS : SV_POSITION;
|
||
float2 uv : TEXCOORD0;
|
||
half4 color : COLOR;
|
||
};
|
||
|
||
TEXTURE2D(_MainTex);
|
||
SAMPLER(sampler_MainTex);
|
||
float4 _MainTex_TexelSize;
|
||
|
||
#if defined(ETC1_EXTERNAL_ALPHA)
|
||
TEXTURE2D(_AlphaTex);
|
||
SAMPLER(sampler_AlphaTex);
|
||
#endif
|
||
|
||
CBUFFER_START(UnityPerMaterial)
|
||
half4 _BaseColor;
|
||
half _BaseAlpha;
|
||
half4 _OutlineColor;
|
||
float _OutlineWidth;
|
||
float _AlphaThreshold;
|
||
float _OutlineSmoothness;
|
||
half4 _RendererColor;
|
||
float4 _Flip;
|
||
float _EnableExternalAlpha;
|
||
CBUFFER_END
|
||
|
||
Varyings vert(Attributes IN)
|
||
{
|
||
Varyings OUT;
|
||
float3 pos = IN.positionOS;
|
||
pos.xy *= _Flip.xy;
|
||
|
||
OUT.positionCS = TransformObjectToHClip(pos);
|
||
OUT.uv = IN.uv;
|
||
|
||
// 注意这里颜色相乘,但不包括 Alpha,Alpha后续单独计算
|
||
OUT.color = IN.color * _BaseColor * _RendererColor;
|
||
return OUT;
|
||
}
|
||
|
||
half SampleSpriteAlpha(float2 uv)
|
||
{
|
||
// 若 UV 超出单张贴图边界(0~1),强制视作完全透明。
|
||
// 这解决了因图片边缘紧贴画布,受 Wrap Mode(Clamp/Repeat) 影响导致无法检测到外部透明度的问题
|
||
if (any(uv < 0.0) || any(uv > 1.0))
|
||
{
|
||
return 0.0;
|
||
}
|
||
|
||
half a = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv).a;
|
||
#if defined(ETC1_EXTERNAL_ALPHA)
|
||
if (_EnableExternalAlpha > 0.0)
|
||
{
|
||
a = SAMPLE_TEXTURE2D(_AlphaTex, sampler_AlphaTex, uv).r;
|
||
}
|
||
#endif
|
||
return a;
|
||
}
|
||
|
||
half3 SampleSpriteRGB(float2 uv)
|
||
{
|
||
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);
|
||
return col.rgb;
|
||
}
|
||
|
||
half4 frag(Varyings IN) : SV_Target
|
||
{
|
||
// 1. 获取当前像素原始Alpha
|
||
half originalAlpha = SampleSpriteAlpha(IN.uv);
|
||
half baseAlphaMultiplier = IN.color.a;
|
||
|
||
// 若原图毫无 Alpha,极其干净地剔除,提高性能
|
||
if (originalAlpha < 0.01)
|
||
{
|
||
return half4(0,0,0,0);
|
||
}
|
||
|
||
#if defined(_OUTLINE_ON)
|
||
// --- 开启描边的情况 ---
|
||
|
||
// 2. 多重采样寻找该像素周围一定范围内,Alpha的最小值
|
||
float2 offsetX = float2(_MainTex_TexelSize.x, 0.0) * _OutlineWidth;
|
||
float2 offsetY = float2(0.0, _MainTex_TexelSize.y) * _OutlineWidth;
|
||
float2 offsetDiag = float2(_MainTex_TexelSize.x, _MainTex_TexelSize.y) * _OutlineWidth * 0.7071;
|
||
|
||
half minAlpha = originalAlpha;
|
||
|
||
minAlpha = min(minAlpha, SampleSpriteAlpha(IN.uv + offsetX));
|
||
minAlpha = min(minAlpha, SampleSpriteAlpha(IN.uv - offsetX));
|
||
minAlpha = min(minAlpha, SampleSpriteAlpha(IN.uv + offsetY));
|
||
minAlpha = min(minAlpha, SampleSpriteAlpha(IN.uv - offsetY));
|
||
|
||
minAlpha = min(minAlpha, SampleSpriteAlpha(IN.uv + offsetDiag));
|
||
minAlpha = min(minAlpha, SampleSpriteAlpha(IN.uv - offsetDiag));
|
||
minAlpha = min(minAlpha, SampleSpriteAlpha(IN.uv + float2(-offsetDiag.x, offsetDiag.y)));
|
||
minAlpha = min(minAlpha, SampleSpriteAlpha(IN.uv + float2(offsetDiag.x, -offsetDiag.y)));
|
||
|
||
// 3. 计算“这是否是一个描边像素” (0:本体, 1:描边)
|
||
half isEdge = 1.0 - smoothstep(_AlphaThreshold - _OutlineSmoothness, _AlphaThreshold + _OutlineSmoothness, minAlpha);
|
||
|
||
// 4. 透明度混合
|
||
half finalObjectAlpha = originalAlpha * baseAlphaMultiplier * _BaseAlpha;
|
||
half finalOutlineAlpha = originalAlpha * baseAlphaMultiplier * _OutlineColor.a;
|
||
half finalAlpha = lerp(finalObjectAlpha, finalOutlineAlpha, isEdge);
|
||
|
||
// 5. 计算颜色
|
||
half3 rawColor = SampleSpriteRGB(IN.uv) * IN.color.rgb; // 包含了BaseColor
|
||
half3 finalRGB = lerp(rawColor, _OutlineColor.rgb, isEdge);
|
||
|
||
return half4(finalRGB, finalAlpha);
|
||
|
||
#else
|
||
// --- 关闭描边的情况,以极低开销渲染原本材质 ---
|
||
|
||
half finalObjectAlpha = originalAlpha * baseAlphaMultiplier * _BaseAlpha;
|
||
half3 rawRGB = SampleSpriteRGB(IN.uv) * IN.color.rgb;
|
||
|
||
return half4(rawRGB, finalObjectAlpha);
|
||
|
||
#endif
|
||
}
|
||
ENDHLSL
|
||
}
|
||
}
|
||
}
|