[Unity] 具有两种形状的 Tint 的棋盘格 Shader

280 阅读7分钟

需求

需要在棋盘格上显示两种形状 Tint,一种是十字形,一种是矩形

具有两种形状的、有移动动画的 Tint 的棋盘格 Shader

本来是想沿用我之前的写法

Shader "Unlit/UnlitGrid"
{
    Properties
    {
        [PerRendererData] _MainTex ("Texture", 2D) = "white" {}
        _MainTexScale ("Main Texture Scale", float) = 1
        _GridSize ("Grid Size", float) = 1
        
        [FloatRange] _GridLineSize ("Grid Line Size", Range(0, 1)) = 0.02
        _LineColor ("Line Color", Color) = (0.7, 0.7, 0.7, 1)
        
        [FloatRange] _TintType ("Tint Type", Range(0, 1)) = 0
        _ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
        _UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)   
        _TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
        _TintRadius("Tint Radius", float) = 1
        _TintPower("Tint Power", float) = 0
        _TintSpreadTime("Tint Spread Time", float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;

            float _MainTexScale;
            
            float _GridSize;
            float _GridLineSize;
            float4 _LineColor;

            float _TintType;
            
            float4 _ReachableTint;
            float4 _UnreachableTint;
            
            vector _TintCenter;
            float _TintRadius;
            float _TintPower;

            float _TintSpreadTime;

            #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            float get_dis(float2 vec)
            {
                float dis_manhattan = abs(vec.x) + abs(vec.y);
                float dis_chebyshev = max(abs(vec.x), abs(vec.y));
                return lerp(dis_manhattan, dis_chebyshev, _TintType);
            }
            
            fixed4 add_tint(fixed4 col, float2 ipos, float2 fpos)
            {
                float itime = floor(_TintSpreadTime);  // integer time
                float ftime = frac(_TintSpreadTime);  // fraction time
                
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = get_dis(tint_center_to_ipos);
                
                float stationary_reachable = step(dist_tint_center_to_ipos, itime + 0.001);
                
                float spreading_reachable = step(dist_tint_center_to_ipos, itime + 1.001) - step(dist_tint_center_to_ipos, itime + 0.001);
                
                // move
                
                float2 move_dir = tint_center_to_ipos/(length(tint_center_to_ipos) + 0.1f);

                float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - ftime/2.0);

                // scale
                
                float2 fpos_to_grid_center = fpos - tint_grid_center;

                float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
                
                spreading_reachable *= step(dist_fpos_to_grid_center, ftime/2.0);

                // add tint

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);
                
                float reachable = (stationary_reachable + spreading_reachable) * radius_reachable;
                
                fixed4 tint = col * _UnreachableTint * (1.0 - reachable) + col * _ReachableTint * reachable;
                
                return lerp(col, tint, _TintPower);
                
            }
            
            fixed4 add_grid(fixed4 col, float2 fpos)
            {
                float scaled_line_size = _GridLineSize/_GridSize;
                
                float2 bl = step(float2(scaled_line_size, scaled_line_size), fpos);       // bottom-left
                float2 tr = step(float2(scaled_line_size, scaled_line_size), float2(1.0, 1.0) - fpos);   // top-right
                float grid_brightness = 1.0 - bl.x * bl.y * tr.x * tr.y;

                col = col * (1.0 - grid_brightness) + _LineColor * grid_brightness;

                return col;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float2 st = i.worldSpacePos.xy / _GridSize;
                float2 st_world = i.worldSpacePos.xy/_MainTexScale;
                
                float2 ipos = floor(st);  // integer
                float2 fpos = frac(st);  // fraction
                
                fixed4 col = tex2D(_MainTex, st_world);
                
                col = add_tint(col, ipos, fpos);
                col = add_grid(col, fpos);
                
                return col;
            }
            ENDCG
        }
    }
}

切换显示 Tint 方法的核心在

            float get_dis(float2 vec)
            {
                float dis_manhattan = abs(vec.x) + abs(vec.y);
                float dis_chebyshev = max(abs(vec.x), abs(vec.y));
                return lerp(dis_manhattan, dis_chebyshev, _TintType);
            }

dis_manhattan 是十字形,dis_chebyshev 是矩形

脚本用法:

private SpriteRenderer spriteRenderer;

private Sequence tintSeq = null;

private void ShowReachableTint(DraggableChess chess)
{
    // 如果已经点选过一次棋子,现在是正在拖动的阶段,那么不用重新显示一遍了
    if(hasClickOnce) return;
    
    Vector3 centerPos = chess.transform.position;
    int reachableSteps = chess.ReachableSteps;
    
    // tint

    spriteRenderer.material.SetVector("_TintCenter", new Vector4(centerPos.x, centerPos.y, 0f, 0f));
    spriteRenderer.material.SetFloat("_TintRadius", reachableSteps);
    spriteRenderer.material.SetFloat("_TintPower", 0f);
    spriteRenderer.material.SetFloat("_TintSpreadTime", 0f);
    
    tintSeq.Pause();
    tintSeq = DOTween.Sequence();
    tintSeq.Insert(0f, spriteRenderer.material.DOFloat(1f, "_TintPower", 0.3f));
    tintSeq.Insert(0f, spriteRenderer.material
        .DOFloat(reachableSteps, "_TintSpreadTime", 1f)
        .SetEase(Ease.OutQuint));

    tintSeq.Play();
}

public void HideReachableTint(DraggableChess chess)
{
    spriteRenderer.material.DOFloat(0f, "_TintPower", 0.3f);
}

cross_tint.gif

rect_tint.gif

矩形 Tint 不使用延展效果

矩形 Tint 如果不使用延展效果的话,Shader 可以写成

Shader "Unlit/UnlitGrid"
{
    Properties
    {
        [PerRendererData] _MainTex ("Texture", 2D) = "white" {}
        _MainTexScale ("Main Texture Scale", float) = 1
        _GridSize ("Grid Size", float) = 1
        
        [FloatRange] _GridLineSize ("Grid Line Size", Range(0, 1)) = 0.02
        _LineColor ("Line Color", Color) = (0.7, 0.7, 0.7, 1)
        
        [FloatRange] _TintType ("Tint Type", Range(0, 1)) = 0
        _ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
        _UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)   
        _TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
        _TintRadius("Tint Radius", float) = 1
        _TintPower("Tint Power", float) = 0
        _TintSpreadTime("Tint Spread Time", float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;

            float _MainTexScale;
            
            float _GridSize;
            float _GridLineSize;
            float4 _LineColor;

            float _TintType;
            
            float4 _ReachableTint;
            float4 _UnreachableTint;
            
            vector _TintCenter;
            float _TintRadius;
            float _TintPower;

            float _TintSpreadTime;

            #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            float get_dis_manhattan(float2 vec)
            {
                return abs(vec.x) + abs(vec.y);
            }

            float get_dis_chebyshev(float2 vec)
            {
                return max(abs(vec.x), abs(vec.y));
            }
            
            fixed4 add_cross_tint(fixed4 col, float2 ipos, float2 fpos)
            {
                float itime = floor(_TintSpreadTime);  // integer time
                float ftime = frac(_TintSpreadTime);  // fraction time
                
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = get_dis_manhattan(tint_center_to_ipos);
                
                float stationary_reachable = step(dist_tint_center_to_ipos, itime + 0.001);
                
                float spreading_reachable = step(dist_tint_center_to_ipos, itime + 1.001) - step(dist_tint_center_to_ipos, itime + 0.001);
                
                // move
                
                float2 move_dir = tint_center_to_ipos/(length(tint_center_to_ipos) + 0.1f);

                float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - ftime/2.0);

                // scale
                
                float2 fpos_to_grid_center = fpos - tint_grid_center;

                float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
                
                spreading_reachable *= step(dist_fpos_to_grid_center, ftime/2.0);

                // add tint

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);
                
                float reachable = (stationary_reachable + spreading_reachable) * radius_reachable;
                
                fixed4 tint = col * _UnreachableTint * (1.0 - reachable) + col * _ReachableTint * reachable;
                
                return lerp(col, tint, _TintPower);
                
            }

            fixed4 add_rect_tint(fixed4 col, float2 ipos)
            {
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = get_dis_chebyshev(tint_center_to_ipos);

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);

                fixed4 tint = col * _UnreachableTint * (1.0 - radius_reachable) + col * _ReachableTint * radius_reachable;

                return lerp(col, tint, _TintPower);
            }
            
            fixed4 add_grid(fixed4 col, float2 fpos)
            {
                float scaled_line_size = _GridLineSize/_GridSize;
                
                float2 bl = step(float2(scaled_line_size, scaled_line_size), fpos);       // bottom-left
                float2 tr = step(float2(scaled_line_size, scaled_line_size), float2(1.0, 1.0) - fpos);   // top-right
                float grid_brightness = 1.0 - bl.x * bl.y * tr.x * tr.y;

                col = col * (1.0 - grid_brightness) + _LineColor * grid_brightness;

                return col;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float2 st = i.worldSpacePos.xy / _GridSize;
                float2 st_world = i.worldSpacePos.xy/_MainTexScale;
                
                float2 ipos = floor(st);  // integer
                float2 fpos = frac(st);  // fraction
                
                fixed4 col = tex2D(_MainTex, st_world);
                
                col = lerp(add_cross_tint(col, ipos, fpos), add_rect_tint(col, ipos), _TintType);
                
                col = add_grid(col, fpos);
                
                return col;
            }
            ENDCG
        }
    }
}

拖动矩形框的脚本示例:

private void OnPropDragBegin(PropChess propChess)
{
    _propChess = propChess;
    
    spriteRenderer.material.SetFloat("_TintType", 1f);
    
    spriteRenderer.material.SetFloat("_TintRadius", propChess.ReachableSteps);
    spriteRenderer.material.SetFloat("_TintPower", 0f);
    
    spriteRenderer.material.DOFloat(1f, "_TintPower", 0.3f);
}

private void OnPropDragEnd()
{
    _propChess = null;
    
    spriteRenderer.material.DOFloat(0f, "_TintPower", 0.3f);
}

void Update()
{
    if(!_propChess) return;
    
    spriteRenderer.material.SetVector("_TintCenter", new Vector4(_propChess.transform.position.x, _propChess.transform.position.y, 0f, 0f));
}

效果:

prop_chess_rect_tint.gif

透明框的尝试

现在策划要求这个 Tint 是覆盖在整个界面的上方的

于是我拆成了两个部分,一个部分是显示棋盘格的,一个部分是盖在世界棋子的上方,提供 Tint 的

画棋盘格的 Shader

Shader "Unlit/UnlitGrid"
{
    Properties
    {
        [PerRendererData] _MainTex ("Texture", 2D) = "white" {}
        _MainTexScale ("Main Texture Scale", float) = 1
        _GridSize ("Grid Size", float) = 1
        
        [FloatRange] _GridLineSize ("Grid Line Size", Range(0, 1)) = 0.02
        _LineColor ("Line Color", Color) = (0.7, 0.7, 0.7, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;

            float _MainTexScale;
            
            float _GridSize;
            float _GridLineSize;
            float4 _LineColor;

            #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }
            
            fixed4 add_grid(fixed4 col, float2 fpos)
            {
                float scaled_line_size = _GridLineSize/_GridSize;
                
                float2 bl = step(float2(scaled_line_size, scaled_line_size), fpos);       // bottom-left
                float2 tr = step(float2(scaled_line_size, scaled_line_size), float2(1.0, 1.0) - fpos);   // top-right
                float grid_brightness = 1.0 - bl.x * bl.y * tr.x * tr.y;

                col = col * (1.0 - grid_brightness) + _LineColor * grid_brightness;

                return col;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float2 st = i.worldSpacePos.xy / _GridSize;
                float2 st_world = i.worldSpacePos.xy/_MainTexScale;
                
                float2 fpos = frac(st);  // fraction
                
                fixed4 col = tex2D(_MainTex, st_world);
                
                col = add_grid(col, fpos);
                
                return col;
            }
            ENDCG
        }
    }
}

画 Tint 的 Shader

Shader "Unlit/UnlitGridTint"
{
    Properties
    {
        [PerRendererData] _MainTex ("Texture", 2D) = "white" {}
        _GridSize ("Grid Size", float) = 1
        
        [FloatRange] _TintType ("Tint Type", Range(0, 1)) = 0
        _ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
        _UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)   
        _TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
        _TintRadius("Tint Radius", float) = 1
        _TintPower("Tint Power", float) = 0
        _TintSpreadTime("Tint Spread Time", float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            float _GridSize;

            float _TintType;
            
            float4 _ReachableTint;
            float4 _UnreachableTint;
            
            vector _TintCenter;
            float _TintRadius;
            float _TintPower;

            float _TintSpreadTime;

            #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            float get_dis_manhattan(float2 vec)
            {
                return abs(vec.x) + abs(vec.y);
            }

            float get_dis_chebyshev(float2 vec)
            {
                return max(abs(vec.x), abs(vec.y));
            }
            
            fixed4 add_cross_tint(float2 ipos, float2 fpos)
            {
                float itime = floor(_TintSpreadTime);  // integer time
                float ftime = frac(_TintSpreadTime);  // fraction time
                
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = get_dis_manhattan(tint_center_to_ipos);
                
                float stationary_reachable = step(dist_tint_center_to_ipos, itime + 0.001);
                
                float spreading_reachable = step(dist_tint_center_to_ipos, itime + 1.001) - step(dist_tint_center_to_ipos, itime + 0.001);
                
                // move
                
                float2 move_dir = tint_center_to_ipos/(length(tint_center_to_ipos) + 0.1f);

                float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - ftime/2.0);

                // scale
                
                float2 fpos_to_grid_center = fpos - tint_grid_center;

                float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
                
                spreading_reachable *= step(dist_fpos_to_grid_center, ftime/2.0);

                // add tint

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);
                
                float reachable = (stationary_reachable + spreading_reachable) * radius_reachable;

                // here dont multi color, because no need
                return lerp(_UnreachableTint, _ReachableTint, reachable);
                
            }

            fixed4 add_rect_tint(float2 ipos)
            {
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = get_dis_chebyshev(tint_center_to_ipos);

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);

                // here dont multi color, because no need
                return lerp(_UnreachableTint, _ReachableTint, radius_reachable);
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float2 st = i.worldSpacePos.xy / _GridSize;
                
                float2 ipos = floor(st);  // integer
                float2 fpos = frac(st);  // fraction
                
                fixed4 col = lerp(add_cross_tint(ipos, fpos), add_rect_tint(ipos), _TintType);

                col.a = _TintPower;
                
                // test
                // col = tex2D(_MainTex, st);
                
                return col;
            }
            ENDCG
        }
    }
}

但是现在出现一个问题,我这个透明的 sprite 会覆盖下面的使用了 sprite-default 材质的棋子,奇怪的是,没有覆盖掉使用了我定义的 Unlit/UnlitGrid 的棋子

以下是我移动这个透明的 sprite 时的效果

trans_mask.gif

可以看到,有些东西被覆盖了,有些东西没有

为了测试,我还用了一个半透明的贴图,然后在片元着色器中输出直接采样得到的 col

// test
col = tex2D(_MainTex, st);

结果还是一样的

我去看了 sprite-default 的写法,感觉大部分都差别不大,除了他还用了一些透明材质,不知道是怎么来的

之后我一个个把官方 Shader 相关的注释试试,发现起作用的是

SubShader
{
    Tags {
        "Queue"="Transparent"
        "IgnoreProjector"="True"
        "RenderType"="Transparent"
        "PreviewType"="Plane" 
    }

中的 "Queue"="Transparent" 这一行

试了一下 Overlay 也行

于是新的 Tint Shader 写成

Shader "Unlit/UnlitGridTint"
{
    Properties
    {
        [PerRendererData] _MainTex ("Texture", 2D) = "white" {}
        _GridSize ("Grid Size", float) = 1
        
        [FloatRange] _TintType ("Tint Type", Range(0, 1)) = 0
        _ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
        _UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)   
        _TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
        _TintRadius("Tint Radius", float) = 1
        _TintPower("Tint Power", float) = 0
        _TintSpreadTime("Tint Spread Time", float) = 0
    }
    SubShader
    {
        Tags {
            "Queue"="Overlay"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane" 
        }
        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            float _GridSize;

            float _TintType;
            
            float4 _ReachableTint;
            float4 _UnreachableTint;
            
            vector _TintCenter;
            float _TintRadius;
            float _TintPower;

            float _TintSpreadTime;

            #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            float get_dis_manhattan(float2 vec)
            {
                return abs(vec.x) + abs(vec.y);
            }

            float get_dis_chebyshev(float2 vec)
            {
                return max(abs(vec.x), abs(vec.y));
            }
            
            fixed4 add_cross_tint(float2 ipos, float2 fpos)
            {
                float itime = floor(_TintSpreadTime);  // integer time
                float ftime = frac(_TintSpreadTime);  // fraction time
                
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = get_dis_manhattan(tint_center_to_ipos);
                
                float stationary_reachable = step(dist_tint_center_to_ipos, itime + 0.001);
                
                float spreading_reachable = step(dist_tint_center_to_ipos, itime + 1.001) - step(dist_tint_center_to_ipos, itime + 0.001);
                
                // move
                
                float2 move_dir = tint_center_to_ipos/(length(tint_center_to_ipos) + 0.1f);

                float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - ftime/2.0);

                // scale
                
                float2 fpos_to_grid_center = fpos - tint_grid_center;

                float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
                
                spreading_reachable *= step(dist_fpos_to_grid_center, ftime/2.0);

                // add tint

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);
                
                float reachable = (stationary_reachable + spreading_reachable) * radius_reachable;

                // here dont multi color, because no need
                return lerp(_UnreachableTint, _ReachableTint, reachable);
                
            }

            fixed4 add_rect_tint(float2 ipos)
            {
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = get_dis_chebyshev(tint_center_to_ipos);

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);

                // here dont multi color, because no need
                return lerp(_UnreachableTint, _ReachableTint, radius_reachable);
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float2 st = i.worldSpacePos.xy / _GridSize;
                
                float2 ipos = floor(st);  // integer
                float2 fpos = frac(st);  // fraction
                
                fixed4 col = lerp(add_cross_tint(ipos, fpos), add_rect_tint(ipos), _TintType);

                col.a = _TintPower;
                
                col.rgb *= col.a;
                
                return col;
            }
            ENDCG
        }
    }
}

效果像这样

trans_mask_2.gif

但是这里有一个问题是,白色的 Tint 会把周围的棋子也染上色

这里主要是片元着色器最后一行强行赋值了一个东西

改成乘等于就好了

col.a *= _TintPower;

最终效果:

trans_mask_3.gif

Tint Shader

Shader "Unlit/UnlitGridTint"
{
    Properties
    {
        [PerRendererData] _MainTex ("Texture", 2D) = "white" {}
        _GridSize ("Grid Size", float) = 1
        
        [FloatRange] _TintType ("Tint Type", Range(0, 1)) = 0
        _ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
        _UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)   
        _TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
        _TintRadius("Tint Radius", float) = 1
        _TintPower("Tint Power", float) = 0
        _TintSpreadTime("Tint Spread Time", float) = 0
    }
    SubShader
    {
        Tags {
            "Queue"="Overlay"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane" 
        }
        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            float _GridSize;

            float _TintType;
            
            float4 _ReachableTint;
            float4 _UnreachableTint;
            
            vector _TintCenter;
            float _TintRadius;
            float _TintPower;

            float _TintSpreadTime;

            #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            float get_dis_manhattan(float2 vec)
            {
                return abs(vec.x) + abs(vec.y);
            }

            float get_dis_chebyshev(float2 vec)
            {
                return max(abs(vec.x), abs(vec.y));
            }
            
            fixed4 add_cross_tint(float2 ipos, float2 fpos)
            {
                float itime = floor(_TintSpreadTime);  // integer time
                float ftime = frac(_TintSpreadTime);  // fraction time
                
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = get_dis_manhattan(tint_center_to_ipos);
                
                float stationary_reachable = step(dist_tint_center_to_ipos, itime + 0.001);
                
                float spreading_reachable = step(dist_tint_center_to_ipos, itime + 1.001) - step(dist_tint_center_to_ipos, itime + 0.001);
                
                // move
                
                float2 move_dir = tint_center_to_ipos/(length(tint_center_to_ipos) + 0.1f);

                float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - ftime/2.0);

                // scale
                
                float2 fpos_to_grid_center = fpos - tint_grid_center;

                float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
                
                spreading_reachable *= step(dist_fpos_to_grid_center, ftime/2.0);

                // add tint

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);
                
                float reachable = (stationary_reachable + spreading_reachable) * radius_reachable;
                
                // here dont multi color, because no need
                return lerp(_UnreachableTint, _ReachableTint, reachable);
                
            }

            fixed4 add_rect_tint(float2 ipos)
            {
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = get_dis_chebyshev(tint_center_to_ipos);

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);

                // here dont multi color, because no need
                return lerp(_UnreachableTint, _ReachableTint, radius_reachable);
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float2 st = i.worldSpacePos.xy / _GridSize;
                
                float2 ipos = floor(st);  // integer
                float2 fpos = frac(st);  // fraction
                
                fixed4 col = lerp(add_cross_tint(ipos, fpos), add_rect_tint(ipos), _TintType);

                col.a *= _TintPower;
                
                col.rgb *= col.a;
                
                return col;
            }
            ENDCG
        }
    }
}