Files
ichni_Creator_Studio/Assets/Shaders/OutlineShape.shader
SoulliesOfficial aee62cd637 大修
2026-03-14 02:30:26 -04:00

191 lines
7.4 KiB
Plaintext
Raw 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.
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
}
}
}