285 lines
12 KiB
Plaintext
285 lines
12 KiB
Plaintext
Shader "Bansonic/Sprite Outline"
|
|
{
|
|
Properties
|
|
{
|
|
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
|
|
[HideInInspector] _Color ("Tint", Color) = (1,1,1,1)
|
|
[HideInInspector] PixelSnap ("Pixel snap", Float) = 0
|
|
[HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
|
|
[HideInInspector] _AlphaTex ("External Alpha", 2D) = "white" {}
|
|
[HideInInspector] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
|
|
|
|
[HDR]_OutlineColor ("Outline Color", Color) = (1,1,1,1)
|
|
_OutlineThickness ("Outline Thickness", Range(0, 12)) = 1
|
|
_AlphaThreshold ("Alpha Threshold", Range(0, 1)) = 0.1
|
|
_OutlineOpacity ("Outline Opacity", Range(0, 1)) = 1
|
|
_InnerOutline ("Inner Outline Blend", Range(0, 1)) = 0
|
|
_SpriteAlpha ("Sprite Alpha", Range(0, 1)) = 1
|
|
}
|
|
|
|
SubShader
|
|
{
|
|
Tags
|
|
{
|
|
"Queue" = "Transparent"
|
|
"RenderType" = "Transparent"
|
|
"RenderPipeline" = "UniversalPipeline"
|
|
"CanUseSpriteAtlas" = "True"
|
|
}
|
|
|
|
Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha
|
|
Cull Off
|
|
ZWrite Off
|
|
|
|
Pass
|
|
{
|
|
Tags { "LightMode" = "Universal2D" }
|
|
|
|
HLSLPROGRAM
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/Core2D.hlsl"
|
|
|
|
#pragma vertex SpriteVertex
|
|
#pragma fragment SpriteFragment
|
|
#pragma target 2.0
|
|
#pragma multi_compile_instancing
|
|
#pragma multi_compile _ SKINNED_SPRITE
|
|
|
|
struct Attributes
|
|
{
|
|
float3 positionOS : POSITION;
|
|
float4 color : COLOR;
|
|
float2 uv : TEXCOORD0;
|
|
UNITY_SKINNED_VERTEX_INPUTS
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
};
|
|
|
|
struct Varyings
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
half4 color : COLOR;
|
|
float2 uv : TEXCOORD0;
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
TEXTURE2D(_MainTex);
|
|
SAMPLER(sampler_MainTex);
|
|
float4 _MainTex_TexelSize;
|
|
|
|
CBUFFER_START(UnityPerMaterial)
|
|
half4 _Color;
|
|
half4 _OutlineColor;
|
|
half _OutlineThickness;
|
|
half _AlphaThreshold;
|
|
half _OutlineOpacity;
|
|
half _InnerOutline;
|
|
half _SpriteAlpha;
|
|
CBUFFER_END
|
|
|
|
Varyings SpriteVertex(Attributes input)
|
|
{
|
|
Varyings output = (Varyings)0;
|
|
UNITY_SETUP_INSTANCE_ID(input);
|
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
|
|
UNITY_SKINNED_VERTEX_COMPUTE(input);
|
|
|
|
SetUpSpriteInstanceProperties();
|
|
input.positionOS = UnityFlipSprite(input.positionOS, unity_SpriteProps.xy);
|
|
output.positionCS = TransformObjectToHClip(input.positionOS);
|
|
output.uv = input.uv;
|
|
output.color = input.color * _Color * unity_SpriteColor;
|
|
return output;
|
|
}
|
|
|
|
half SampleAlpha(float2 uv, half4 tint)
|
|
{
|
|
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv) * tint;
|
|
return tex.a;
|
|
}
|
|
|
|
half ComputeOuterMask(float2 uv, half4 tint)
|
|
{
|
|
float2 texel = _MainTex_TexelSize.xy * max(_OutlineThickness, 0.001h);
|
|
half center = SampleAlpha(uv, tint);
|
|
half maxNeighbor = 0h;
|
|
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 1.0, 0.0), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2(-1.0, 0.0), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 0.0, 1.0), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 0.0, -1.0), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 0.7071, 0.7071), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2(-0.7071, 0.7071), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 0.7071, -0.7071), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2(-0.7071, -0.7071), tint));
|
|
|
|
half centerMask = step(_AlphaThreshold, center);
|
|
half neighborMask = step(_AlphaThreshold, maxNeighbor);
|
|
return saturate(neighborMask - centerMask);
|
|
}
|
|
|
|
half ComputeInnerMask(float2 uv, half4 tint)
|
|
{
|
|
float2 texel = _MainTex_TexelSize.xy * max(_OutlineThickness, 0.001h);
|
|
half center = SampleAlpha(uv, tint);
|
|
half minNeighbor = 1h;
|
|
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 1.0, 0.0), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2(-1.0, 0.0), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 0.0, 1.0), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 0.0, -1.0), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 0.7071, 0.7071), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2(-0.7071, 0.7071), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 0.7071, -0.7071), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2(-0.7071, -0.7071), tint));
|
|
|
|
half centerMask = step(_AlphaThreshold, center);
|
|
half neighborMask = step(_AlphaThreshold, minNeighbor);
|
|
return saturate(centerMask - neighborMask);
|
|
}
|
|
|
|
half4 SpriteFragment(Varyings input) : SV_Target
|
|
{
|
|
half4 baseSample = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv) * input.color;
|
|
half outerMask = ComputeOuterMask(input.uv, input.color);
|
|
half innerMask = ComputeInnerMask(input.uv, input.color);
|
|
half outlineMask = saturate(lerp(outerMask, max(outerMask, innerMask), _InnerOutline)) * _OutlineOpacity;
|
|
|
|
half4 color = baseSample;
|
|
color.rgb *= _SpriteAlpha;
|
|
color.a *= _SpriteAlpha;
|
|
|
|
color.rgb = lerp(color.rgb, _OutlineColor.rgb, outlineMask * _OutlineColor.a);
|
|
color.a = saturate(color.a + outlineMask * _OutlineColor.a);
|
|
return color;
|
|
}
|
|
ENDHLSL
|
|
}
|
|
|
|
Pass
|
|
{
|
|
Tags { "LightMode" = "UniversalForward" }
|
|
|
|
HLSLPROGRAM
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/Core2D.hlsl"
|
|
|
|
#pragma vertex SpriteVertex
|
|
#pragma fragment SpriteFragment
|
|
#pragma target 2.0
|
|
#pragma multi_compile_instancing
|
|
#pragma multi_compile _ SKINNED_SPRITE
|
|
|
|
struct Attributes
|
|
{
|
|
float3 positionOS : POSITION;
|
|
float4 color : COLOR;
|
|
float2 uv : TEXCOORD0;
|
|
UNITY_SKINNED_VERTEX_INPUTS
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
};
|
|
|
|
struct Varyings
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
half4 color : COLOR;
|
|
float2 uv : TEXCOORD0;
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
TEXTURE2D(_MainTex);
|
|
SAMPLER(sampler_MainTex);
|
|
float4 _MainTex_TexelSize;
|
|
|
|
CBUFFER_START(UnityPerMaterial)
|
|
half4 _Color;
|
|
half4 _OutlineColor;
|
|
half _OutlineThickness;
|
|
half _AlphaThreshold;
|
|
half _OutlineOpacity;
|
|
half _InnerOutline;
|
|
half _SpriteAlpha;
|
|
CBUFFER_END
|
|
|
|
Varyings SpriteVertex(Attributes input)
|
|
{
|
|
Varyings output = (Varyings)0;
|
|
UNITY_SETUP_INSTANCE_ID(input);
|
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
|
|
UNITY_SKINNED_VERTEX_COMPUTE(input);
|
|
|
|
SetUpSpriteInstanceProperties();
|
|
input.positionOS = UnityFlipSprite(input.positionOS, unity_SpriteProps.xy);
|
|
output.positionCS = TransformObjectToHClip(input.positionOS);
|
|
output.uv = input.uv;
|
|
output.color = input.color * _Color * unity_SpriteColor;
|
|
return output;
|
|
}
|
|
|
|
half SampleAlpha(float2 uv, half4 tint)
|
|
{
|
|
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv) * tint;
|
|
return tex.a;
|
|
}
|
|
|
|
half ComputeOuterMask(float2 uv, half4 tint)
|
|
{
|
|
float2 texel = _MainTex_TexelSize.xy * max(_OutlineThickness, 0.001h);
|
|
half center = SampleAlpha(uv, tint);
|
|
half maxNeighbor = 0h;
|
|
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 1.0, 0.0), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2(-1.0, 0.0), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 0.0, 1.0), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 0.0, -1.0), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 0.7071, 0.7071), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2(-0.7071, 0.7071), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2( 0.7071, -0.7071), tint));
|
|
maxNeighbor = max(maxNeighbor, SampleAlpha(uv + texel * float2(-0.7071, -0.7071), tint));
|
|
|
|
half centerMask = step(_AlphaThreshold, center);
|
|
half neighborMask = step(_AlphaThreshold, maxNeighbor);
|
|
return saturate(neighborMask - centerMask);
|
|
}
|
|
|
|
half ComputeInnerMask(float2 uv, half4 tint)
|
|
{
|
|
float2 texel = _MainTex_TexelSize.xy * max(_OutlineThickness, 0.001h);
|
|
half center = SampleAlpha(uv, tint);
|
|
half minNeighbor = 1h;
|
|
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 1.0, 0.0), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2(-1.0, 0.0), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 0.0, 1.0), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 0.0, -1.0), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 0.7071, 0.7071), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2(-0.7071, 0.7071), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2( 0.7071, -0.7071), tint));
|
|
minNeighbor = min(minNeighbor, SampleAlpha(uv + texel * float2(-0.7071, -0.7071), tint));
|
|
|
|
half centerMask = step(_AlphaThreshold, center);
|
|
half neighborMask = step(_AlphaThreshold, minNeighbor);
|
|
return saturate(centerMask - neighborMask);
|
|
}
|
|
|
|
half4 SpriteFragment(Varyings input) : SV_Target
|
|
{
|
|
half4 baseSample = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv) * input.color;
|
|
half outerMask = ComputeOuterMask(input.uv, input.color);
|
|
half innerMask = ComputeInnerMask(input.uv, input.color);
|
|
half outlineMask = saturate(lerp(outerMask, max(outerMask, innerMask), _InnerOutline)) * _OutlineOpacity;
|
|
|
|
half4 color = baseSample;
|
|
color.rgb *= _SpriteAlpha;
|
|
color.a *= _SpriteAlpha;
|
|
|
|
color.rgb = lerp(color.rgb, _OutlineColor.rgb, outlineMask * _OutlineColor.a);
|
|
color.a = saturate(color.a + outlineMask * _OutlineColor.a);
|
|
return color;
|
|
}
|
|
ENDHLSL
|
|
}
|
|
}
|
|
|
|
FallBack "Sprites/Default"
|
|
}
|