# 【多边形边缘虚化】

全屏显示

PolygonFadeShader.shader

Shader "Unlit/PolygonFadeShader"
{
    Properties
    {
        _Point1 ("Point1", Vector) =  (0.5,0.319336,0,0)
        _Point2 ("Point2", Vector) =  (0.691406,0.457031,0,0)
        _Point3 ("Point3", Vector) =  (0.618164,0.679688,0,0)
        _Point4 ("Point4", Vector) =  (0.380859,0.679688,0,0)
        _Point5 ("Point5", Vector) =  (0.308594,0.457031,0,0)

        // 遍历半径
        _FadeRange("FadeRange", Range(0, 0.2)) = 0.1
        // 遍历的圈数,圈数越多,性能越差,渐变精确度越高
        _ForRounds("ForRounds", Range(4, 20)) = 10
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            uniform float4 _Point1;
            uniform float4 _Point2;
            uniform float4 _Point3;
            uniform float4 _Point4;
            uniform float4 _Point5;

            uniform float _FadeRange;
            uniform float _ForRounds;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            // 检查UV点是否位于多边形内部,是返回1,否返回0
            float checkInPolygon(float2 st)
            {
                int numIntersections = 0;
                float2 p1 = _Point1.xy;
                float2 p2 = _Point2.xy;
                float2 p3 = _Point3.xy;
                float2 p4 = _Point4.xy;
                float2 p5 = _Point5.xy;
                
                // 检查在多边形上的交点数量
                numIntersections += (st.y > p1.y) != (st.y > p2.y) && (st.x < (p2.x - p1.x) * (st.y - p1.y) / (p2.y - p1.y) + p1.x) ? 1 : 0;
                numIntersections += (st.y > p2.y) != (st.y > p3.y) && (st.x < (p3.x - p2.x) * (st.y - p2.y) / (p3.y - p2.y) + p2.x) ? 1 : 0;
                numIntersections += (st.y > p3.y) != (st.y > p4.y) && (st.x < (p4.x - p3.x) * (st.y - p3.y) / (p4.y - p3.y) + p3.x) ? 1 : 0;
                numIntersections += (st.y > p4.y) != (st.y > p5.y) && (st.x < (p5.x - p4.x) * (st.y - p4.y) / (p5.y - p4.y) + p4.x) ? 1 : 0;
                numIntersections += (st.y > p5.y) != (st.y > p1.y) && (st.x < (p1.x - p5.x) * (st.y - p5.y) / (p1.y - p5.y) + p5.x) ? 1 : 0;
                
                // 交点数为奇数时,为多边形内部
                return numIntersections % 2 == 1 ? 1 : 0;
            }

            // 检查是否在边缘,根据距离 0~1的值
            float checkInFade(float2 st)
            {
                float pi2 = 2 * 3.1415926;

                // 单个检测小圆的直径
                float singleDiameter = _FadeRange/_ForRounds;
                float singleRadius = singleDiameter/2;// 单个检测小圆半径

                float minDis = 999999;
                float bi = 0;
                int i = 0;
                int j = 0;
                int exit = 0;// 用于节省性能,找到最近的距离后,退出for循环
                for(i = 0; i < _ForRounds;++i)
                {
                    if(exit == 1){
                        break;
                    }
                    // 半径
                    float radius = i * singleDiameter + singleRadius;
                    // 周长
                    float perimeter = pi2 * radius;

                    // 当前圈拆分成的检测点数量
                    int count = ceil(perimeter/singleDiameter);
                    for(j = 0; j < count;++j)
                    {
                        // 角度
                        float angleInRadin = float(j)/count * pi2;
                        
                        // 角度转坐标
                        float x = st.x + cos(angleInRadin) * radius;
                        float y = st.y + sin(angleInRadin) * radius;
                        float2 st2 = float2(x, y);
                        float check = checkInPolygon(st2);
                        if(check == 1)
                        {
                            float dis = distance(st, st2);
                            if(dis < minDis){
                                minDis = dis;
                                bi = 1 - dis/_FadeRange;
                                exit = 1;
                                break ;
                            }
                        }
                    }
                }
                return bi;
            }

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float result = checkInPolygon(i.uv);
                if(result == 0){
                    result = checkInFade(i.uv);
                }
                return result;
            }
            ENDCG
        }
    }
}