Unity 制作类似RadiusFill 的SpriteRanderer Shader

发表于2018-03-06
评论0 1.3k浏览
先搞到SpriteDefault的官方Shader,然后开始修改。

Shader "Sprites/Fill"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
注:这里做了第一处修改,代码里面可以通过SetFloat("_Fill",0.5f) 来修改这个值。
这个接口的意义,输入0-1,绘制0度到360度的贴图
如图,这是参数为0.32时候绘制的贴图。有了他就可以在游戏内实现
类似技能CD转圈提示的效果了。

        _Fill ("Fill", Range(0,1)) = 0
    }
    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }
        Cull Off
        Lighting Off
        ZWrite Off
        Fog { Mode Off }
        Blend SrcAlpha OneMinusSrcAlpha
        Pass
        {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile DUMMY PIXELSNAP_ON
            #include "UnityCG.cginc"
            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };
            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                half2 texcoord  : TEXCOORD0;
            };
            fixed4 _Color;
注:这里接收上面传进来的_Fill值,语法就不多啰嗦了。

            float _Fill;
            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif
                return OUT;
            }
            sampler2D _MainTex;
            fixed4 frag(v2f IN) : COLOR
            {
                
注:这里才是正题。有多种方法,判定一个点是不是在从0度,到N度之间的范围之内。
比如求点到圆心的向量到正方向的单位向量的点积等等。
最终我选择了求Atan值并且分区间求解的方案,因为计算量相对小,更主要是精度高。
没有噪点。

                fixed4 result = tex2D(_MainTex, IN.texcoord) * IN.color;
注:这一步相当于是把任意的UV坐标点,转换为以0.0为中心,-0.5 到 0.5范围的点。

               fixed2 p = fixed2( IN.texcoord.x - 0.5, IN.texcoord.y - 0.5);
注:这相当于是分两段计算,因为直接取atan的时候,左右两边会有相同的值,没法判断左半圈还是右半圈。

                if( _Fill< 0.5 )
                {
注:将_Fill 0-0.5的值,映射给0-pie,只处理左半边,右半边全部按照 alpha = 0处理

                    float compare = ( _Fill * 2 - 0.5) * 3.1415926;
                    float theta = atan( p.y / p.x );
                    if( theta > compare )
                    {
                        result.a = 0;
                    }
                    if( p.x>0 )
                    {
                        result.a = 0;
                    }
                }
                else
                {
注:将_Fill 0.5-1映射给0-pie,左半边按照 alpha = alpha * 1处理。只计算右半边。
因此关键点只有1点,就是计算 atan 以及将问题分治解决,将_Fill分两段分别映射。

                    float compare = ((_Fill-0.5) * 2 -0.5) * 3.1415926;
                    float theta = atan( p.y / p.x );
                    if( p.x > 0 )
                    {
                        if( theta > compare )
                        {
                            result.a = 0;
                        }
                    }
                }
                return result;
            }
        ENDCG
        }
    }
}

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引