Created
June 24, 2021 08:07
-
-
Save heppoko/d6bc3dcd758956364d54f10d35041b94 to your computer and use it in GitHub Desktop.
uGUI のアウトラインをシェーダーで実現する
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Shader "UI/Outline" | |
{ | |
Properties | |
{ | |
[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {} | |
_Color("Tint", Color) = (1,1,1,1) | |
// アウトラインの色 | |
_EffectColor("Effect Color", Color) = (0, 0, 0, 0.5) | |
// アウトラインの距離 | |
_EffectDistance("Effect Distance", Float) = 1 | |
_StencilComp("Stencil Comparison", Float) = 8 | |
_Stencil("Stencil ID", Float) = 0 | |
_StencilOp("Stencil Operation", Float) = 0 | |
_StencilWriteMask("Stencil Write Mask", Float) = 255 | |
_StencilReadMask("Stencil Read Mask", Float) = 255 | |
_ColorMask("Color Mask", Float) = 15 | |
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0 | |
} | |
SubShader | |
{ | |
Tags | |
{ | |
"Queue" = "Transparent" | |
"IgnoreProjector" = "True" | |
"RenderType" = "Transparent" | |
"PreviewType" = "Plane" | |
"CanUseSpriteAtlas" = "True" | |
} | |
Stencil | |
{ | |
Ref[_Stencil] | |
Comp[_StencilComp] | |
Pass[_StencilOp] | |
ReadMask[_StencilReadMask] | |
WriteMask[_StencilWriteMask] | |
} | |
Cull Off | |
Lighting Off | |
ZWrite Off | |
ZTest[unity_GUIZTestMode] | |
Blend One OneMinusSrcAlpha | |
ColorMask[_ColorMask] | |
Pass | |
{ | |
Name "Default" | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
#pragma target 2.0 | |
#include "UnityCG.cginc" | |
#include "UnityUI.cginc" | |
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT | |
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP | |
struct appdata_t | |
{ | |
float4 vertex : POSITION; | |
float4 color : COLOR; | |
float2 texcoord : TEXCOORD0; | |
UNITY_VERTEX_INPUT_INSTANCE_ID | |
}; | |
struct v2f | |
{ | |
float4 vertex : SV_POSITION; | |
fixed4 color : COLOR; | |
float2 texcoord : TEXCOORD0; | |
float4 worldPosition : TEXCOORD1; | |
half4 mask : TEXCOORD2; | |
UNITY_VERTEX_OUTPUT_STEREO | |
}; | |
sampler2D _MainTex; | |
fixed4 _Color; | |
fixed4 _TextureSampleAdd; | |
float4 _ClipRect; | |
float4 _MainTex_ST; | |
float _MaskSoftnessX; | |
float _MaskSoftnessY; | |
// 色と距離をプロパティから受け取る | |
half4 _EffectColor; | |
half _EffectDistance; | |
// テクスチャサイズ | |
// x : 1 / width | |
// y : 1 / height | |
// z : width | |
// w : height | |
float4 _MainTex_TexelSize; | |
// 頂点シェーダーはそのまま | |
v2f vert(appdata_t v) | |
{ | |
v2f OUT; | |
UNITY_SETUP_INSTANCE_ID(v); | |
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); | |
float4 vPosition = UnityObjectToClipPos(v.vertex); | |
OUT.worldPosition = v.vertex; | |
OUT.vertex = vPosition; | |
float2 pixelSize = vPosition.w; | |
pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy)); | |
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10); | |
float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy); | |
OUT.texcoord = float4(v.texcoord.x, v.texcoord.y, maskUV.x, maskUV.y); | |
OUT.mask = half4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + abs(pixelSize.xy))); | |
OUT.color = v.color * _Color; | |
return OUT; | |
} | |
// フラグメントシェーダーでのアウトラインの処理 | |
half4 frag_outline(v2f IN, half4 color) | |
{ | |
// 元のアルファ | |
half original_a = color.a; | |
// 元のアルファが 1 なら元の色にして、元のアルファが 0 なら一旦アウトラインの色にする | |
color = lerp(_EffectColor, color, original_a); | |
float delta_x = _MainTex_TexelSize.x * _EffectDistance; | |
float delta_y = _MainTex_TexelSize.y * _EffectDistance; | |
// 上下左右のピクセルの色を得る | |
half4 color_top = tex2D(_MainTex, IN.texcoord + float2(0, delta_y)); | |
half4 color_bottom = tex2D(_MainTex, IN.texcoord - float2(0, delta_y)); | |
half4 color_left = tex2D(_MainTex, IN.texcoord - float2(delta_x, 0)); | |
half4 color_right = tex2D(_MainTex, IN.texcoord + float2(delta_x, 0)); | |
// 斜めの点の距離は √2 / 2 倍 | |
delta_x *= 0.707; | |
delta_y *= 0.707; | |
// 左上、左下、右上、右下のピクセルの色を得る | |
half4 color_left_top = tex2D(_MainTex, IN.texcoord - float2(delta_x, -delta_y)); | |
half4 color_left_bottom = tex2D(_MainTex, IN.texcoord - float2(delta_x, delta_y)); | |
half4 color_right_top = tex2D(_MainTex, IN.texcoord + float2(delta_x, delta_y)); | |
half4 color_right_bottom = tex2D(_MainTex, IN.texcoord + float2(delta_x, -delta_y)); | |
// 元のピクセルおよび周囲のピクセルの中でで一番アルファが濃いピクセルのアルファを使う | |
color.a = max(original_a, max(max(max(color_top.a, color_bottom.a), max(color_left.a, color_right.a)), | |
max(max(color_left_top.a, color_left_bottom.a), max(color_right_top.a, color_right_bottom.a)))); | |
return color; | |
} | |
fixed4 frag(v2f IN) : SV_Target | |
{ | |
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; | |
#ifdef UNITY_UI_CLIP_RECT | |
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw); | |
color.a *= m.x * m.y; | |
#endif | |
// アウトラインの適用 | |
color = frag_outline(IN, color); | |
#ifdef UNITY_UI_ALPHACLIP | |
clip(color.a - 0.001); | |
#endif | |
color.rgb *= color.a; | |
return color; | |
} | |
ENDCG | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment