Files
ichni_Creator_Studio/Assets/Shaders/OutlineShape.shader

191 lines
7.4 KiB
Plaintext
Raw Normal View History

2026-03-14 02:30:26 -04:00
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;
// 注意这里颜色相乘,但不包括 AlphaAlpha后续单独计算
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
}
}
}