Files
Outline-Toolkit/Runtime/Shaders/Outline.shader
2025-07-20 21:01:09 +08:00

197 lines
4.2 KiB
Plaintext

// Copyright (C) 2019-2021 Alexander Bogarsukov. All rights reserved.
// See the LICENSE.md file in the project root for more information.
// Renders outline based on a texture produced with 'UnityF/OutlineColor'.
// Modified version of 'Custom/Post Outline' shader taken from https://willweissman.wordpress.com/tutorials/shaders/unity-shaderlab-object-outlines/.
Shader "Hidden/UnityFx/Outline"
{
HLSLINCLUDE
#include "UnityCG.cginc"
UNITY_DECLARE_SCREENSPACE_TEXTURE(_MaskTex);
UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex);
float2 _MainTex_TexelSize;
float4 _Color;
float _Intensity;
int _Width;
float _GaussSamples[32];
#if SHADER_TARGET < 35 || _USE_DRAWMESH
v2f_img vert(appdata_img v)
{
v2f_img o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_OUTPUT(v2f_img, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = float4(v.vertex.xy, UNITY_NEAR_CLIP_VALUE, 1);
o.uv = ComputeScreenPos(o.pos);
return o;
}
#else
struct appdata_vid
{
uint vertexID : SV_VertexID;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
float4 GetFullScreenTriangleVertexPosition(uint vertexID, float z = UNITY_NEAR_CLIP_VALUE)
{
// Generates a triangle in homogeneous clip space, s.t.
// v0 = (-1, -1, 1), v1 = (3, -1, 1), v2 = (-1, 3, 1).
float2 uv = float2((vertexID << 1) & 2, vertexID & 2);
return float4(uv * 2 - 1, z, 1);
}
v2f_img vert(appdata_vid v)
{
v2f_img o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_OUTPUT(v2f_img, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = GetFullScreenTriangleVertexPosition(v.vertexID);
o.uv = ComputeScreenPos(o.pos);
return o;
}
#endif
float CalcIntensityN0(float2 uv, float2 offset, int k)
{
return UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, uv + k * offset).r * _GaussSamples[k];
}
float CalcIntensityN1(float2 uv, float2 offset, int k)
{
return UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, uv - k * offset).r * _GaussSamples[k];
}
float CalcIntensity(float2 uv, float2 offset)
{
float intensity = 0;
// Accumulates horizontal or vertical blur intensity for the specified texture position.
// Set offset = (tx, 0) for horizontal sampling and offset = (0, ty) for vertical.
//
// NOTE: Unroll directive is needed to make the method function on platforms like WebGL 1.0 where loops are not supported.
// If maximum outline width is changed here, it should be changed in OutlineResources.MaxWidth as well.
//
[unroll(32)]
for (int k = 1; k <= _Width; ++k)
{
intensity += CalcIntensityN0(uv, offset, k);
intensity += CalcIntensityN1(uv, offset, k);
}
intensity += CalcIntensityN0(uv, offset, 0);
return intensity;
}
float4 frag_h(v2f_img i) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
float intensity = CalcIntensity(i.uv, float2(_MainTex_TexelSize.x, 0));
return float4(intensity, intensity, intensity, 1);
}
float4 frag_v(v2f_img i) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
if (UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MaskTex, i.uv).r > 0)
{
// TODO: Avoid discard/clip to improve performance on mobiles.
discard;
}
float intensity = CalcIntensity(i.uv, float2(0, _MainTex_TexelSize.y));
intensity = _Intensity > 99 ? step(0.01, intensity) : intensity * _Intensity;
return float4(_Color.rgb, saturate(_Color.a * intensity));
}
ENDHLSL
// SM3.5+
SubShader
{
Cull Off
ZWrite Off
ZTest Always
Lighting Off
Pass
{
Name "HPass"
HLSLPROGRAM
#pragma target 3.5
#pragma multi_compile_instancing
#pragma shader_feature_local _USE_DRAWMESH
#pragma vertex vert
#pragma fragment frag_h
ENDHLSL
}
Pass
{
Name "VPassBlend"
Blend SrcAlpha OneMinusSrcAlpha
HLSLPROGRAM
#pragma target 3.5
#pragma multi_compile_instancing
#pragma shader_feature_local _USE_DRAWMESH
#pragma vertex vert
#pragma fragment frag_v
ENDHLSL
}
}
// SM2.0
SubShader
{
Cull Off
ZWrite Off
ZTest Always
Lighting Off
Pass
{
Name "HPass"
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag_h
ENDHLSL
}
Pass
{
Name "VPassBlend"
Blend SrcAlpha OneMinusSrcAlpha
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag_v
ENDHLSL
}
}
}