Unity shader 实现图片带圆角和边线border

本文参考了文章Unity shader 实现图片带圆角和边线border

并在上文基础上增加了功能。圆角区域不会再被点击到。

效果如下:

使用方法:

点击添加方法即可。如图

Corner4==圆角大小,Alpha==中心区域透明度,BorderWidth==描边宽度,BorderColor==描边颜色

shader:

Shader "Custom/UI/RoundConor"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Alpha("Alpha", Range(0, 1)) = 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

        _RoundedCorner("Round",Vector)=(8,8,8,8)
        _Width("View Width", Float) = 200
        _Height("View Height", Float) = 200
        _BorderWidth("Border Width", Float) = 1
        _BorderColor("Boader Color", Color) = (1, 0, 0, 1)
    }

    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 SrcAlpha OneMinusSrcAlpha
        ColorMask[_ColorMask]

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "UnityUI.cginc"

            #pragma multi_compile __ UNITY_UI_ALPHACLIP

            struct appdata_t
            {
                float4 vertex :
                POSITION;
                float4 color :
                COLOR;
                float2 texcoord :
                TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex :
                SV_POSITION;
                fixed4 color :
                COLOR;
                half2 texcoord :
                TEXCOORD0;
                float4 worldPosition :
                TEXCOORD1;
            };
 float _Alpha;

            fixed4 _TextureSampleAdd;
            float4 _ClipRect;

            float4 _RoundedCorner;
            float _Width;
            float _Height;
            float _BorderWidth;
            float4 _BorderColor;

            float4 _MainTex_TexelSize; //纹理的大小,可能没有纹理,只有顶点颜色

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.worldPosition = IN.vertex;
                OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                OUT.texcoord = IN.texcoord;

                OUT.color = IN.color;
                return OUT;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = IN.color;
                if (_MainTex_TexelSize.z > 0)
                {
                    //有纹理,则颜色从纹理读取, 并叠加顶点颜色
                    color = (tex2D(_MainTex, IN.texcoord)) * IN.color;
                }
               color.a=color.a= _Alpha;
                //float width = _MainTex_TexelSize.z;
                //float height = _MainTex_TexelSize.w;

                float width = _Width;
                float height = _Height;

                if (width <= 0 && _MainTex_TexelSize.z > 0)
                {
                    //如果没定义宽度,而纹理又定义了宽度,则从纹理宽度读取
                    width = _MainTex_TexelSize.z;
                }
                if (height <= 0 && _MainTex_TexelSize.w > 0)
                {
                    //同上
                    height = _MainTex_TexelSize.w;
                }

                float border_width = _BorderWidth;
                half4 border_color = _BorderColor;

                float x = IN.texcoord.x * width;
                float y = IN.texcoord.y * height;


                float arc_size = 0;
                float r = _RoundedCorner.w;
                //左下角
                if (x < r && y < r)
                {
                    arc_size = (x - r) * (x - r) + (y - r) * (y - r);
                    if (arc_size > r * r)
                    {
                        color.a = 0;
                    }
                    else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width))
                    {
                        color = border_color;
                    }
                }
                r = _RoundedCorner.x;
                //左上角
                if (x < r && y > (height - r))
                {
                    arc_size = (x - r) * (x - r) + (y - (height - r)) * (y - (height - r));
                    if (arc_size > r * r)
                    {
                        color.a = 0;
                    }
                    else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width))
                    {
                        color = border_color;
                    }
                }
                r = _RoundedCorner.z;
                //右下角
                if (x > (width - r) && y < r)
                {
                    arc_size = (x - (width - r)) * (x - (width - r)) + (y - r) * (y - r);
                    if (arc_size > r * r)
                    {
                        color.a = 0;
                    }
                    else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width))
                    {
                        color = border_color;
                    }
                }
                r = _RoundedCorner.y;
                //右上角
                if (x > (width - r) && y > (height - r))
                {
                    arc_size = (x - (width - r)) * (x - (width - r)) + (y - (height - r)) * (y - (height - r));
                    if (arc_size > r * r)
                    {
                        color.a = 0;
                    }
                    else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width))
                    {
                        color = border_color;
                    }
                }

                if (border_width > 0)
                {
                    //下边直线区域
                    if (x > _RoundedCorner.w && x < (width - _RoundedCorner.z) && y < border_width)
                    {
                        color = border_color;
                    }
                    //上边直线区域
                    if (x > _RoundedCorner.x && x < (width - _RoundedCorner.y) && (height - y) < border_width)
                    {
                        color = border_color;
                    }
                    //左边直线区域
                    if (y > _RoundedCorner.w && y < (height - _RoundedCorner.x) && x < border_width)
                    {
                        color = border_color;
                    }
                    //右边直线区域
                    if (y > _RoundedCorner.z && y < (height - _RoundedCorner.y) && x > (width - border_width))
                    {
                        color = border_color;
                    }
                }

                return color;
            }
            ENDCG
        }
    }
}

代码:

namespace UnityEngine.UI
{
    public class UIRoundConor_RawImage : RawImage
    {
        const float defaultCorner = 4;

        [SerializeField]
        protected Vector4 m_Corner4 = new Vector4(defaultCorner, defaultCorner, defaultCorner, defaultCorner);

        [SerializeField][Range(0,1)] protected float m_Alpha = 1;
        [SerializeField][Range(0,255)] protected float m_BorderWidth = 1;
        [SerializeField] protected Color m_BorderColor = Color.black;
        private static readonly int RoundedCornerID = Shader.PropertyToID("_RoundedCorner");
        private static readonly int BorderWidthID = Shader.PropertyToID("_BorderWidth");
        private static readonly int BorderColorID = Shader.PropertyToID("_BorderColor");
        private static readonly int WidthID = Shader.PropertyToID("_Width");
        private static readonly int HeightID = Shader.PropertyToID("_Height");
        private static readonly int AlphaID = Shader.PropertyToID("_Alpha");

        /// <summary>
        /// 角点
        /// x=topLeft
        /// y=topRight
        /// z=bottomRight
        /// w=bottomLeft
        /// </summary>
        public Vector4 Corner4
        {
            get => m_Corner4;
            set
            {
                m_Corner4 = value;
                UpdateMaterial();
            }
        }

        /// <summary>
        /// 中心区域透明度
        /// </summary>
        public float Alpha
        {
            get => m_Alpha;
            set
            {
                m_Alpha = value;
                UpdateMaterial();
            }
        }

        /// <summary>
        /// 描边宽度
        /// </summary>
        public float BorderWidth
        {
            get => m_BorderWidth;
            set
            {
                m_BorderWidth = value;
                UpdateMaterial();
            }
        }

        /// <summary>
        /// 描边颜色
        /// </summary>
        public Color BorderColor
        {
            get => m_BorderColor;
            set
            {
                m_BorderColor = value;
                UpdateMaterial();
            }
        }

        protected override void Awake()
        {
            var shader = Shader.Find("Custom/UI/RoundConor");
            this.material = new Material(shader);
            UpdateMaterial();
        }

        /// <summary>
        /// 更新材料
        /// </summary>
        protected override void UpdateMaterial()
        {
            base.UpdateMaterial();
            material.SetVector(RoundedCornerID, m_Corner4);
            material.SetFloat(AlphaID, m_Alpha);
            material.SetFloat(BorderWidthID, m_BorderWidth);
            material.SetColor(BorderColorID, m_BorderColor);
            material.SetFloat(WidthID, rectTransform.rect.size.x);
            material.SetFloat(HeightID, rectTransform.rect.size.y);
        }

        /// <summary>
        /// 设置点击区域,圆角区域不可被点击
        /// </summary>
        /// <param name="sp"></param>
        /// <param name="eventCamera"></param>
        /// <returns></returns>
        public override bool Raycast(Vector2 sp, Camera eventCamera)
        {
            Vector2 localPoint;
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, sp, eventCamera, out localPoint))
            {
                if (!float.IsNaN(localPoint.x)&&!float.IsNaN(localPoint.y))
                {
                    var realCorner = m_Corner4;
                    var localRect = rectTransform.rect;

                    var topLeft = new Vector2(localRect.xMin + realCorner.x, localRect.yMin + realCorner.x);
                    if (localPoint.x < topLeft.x && localPoint.y < topLeft.y)
                    {
                        if (Vector2.Distance(topLeft, localPoint) > realCorner.x)
                        {
                            return false;
                        }
                    }

                    var topRight = new Vector2(localRect.xMax - realCorner.y, localRect.yMin + realCorner.y);
                    if (localPoint.x > topRight.x && localPoint.y < topRight.y)
                    {
                        if (Vector2.Distance(topRight, localPoint) > realCorner.y)
                        {
                            return false;
                        }
                    }

                    var bottomRight = new Vector2(localRect.xMax - realCorner.z, localRect.yMax - realCorner.z);
                    if (localPoint.x > bottomRight.x && localPoint.y > bottomRight.y)
                    {
                        if (Vector2.Distance(bottomRight, localPoint) > realCorner.z)
                        {
                            return false;
                        }
                    }

                    var bottomLeft = new Vector2(localRect.xMin + realCorner.w, localRect.yMax - realCorner.w);
                    if (localPoint.x < bottomLeft.x && localPoint.y > bottomLeft.y)
                    {
                        if (Vector2.Distance(bottomLeft, localPoint) > realCorner.w)
                        {
                            return false;
                        }
                    }
                }
            }

            return base.Raycast(sp, eventCamera);
        }
    }
}
using UnityEditor;
using UnityEditor.UI;
using UnityEngine.UI;

[CustomEditor(typeof(UIRoundConor_RawImage), true)]
public class UIRoundConor_RawImageEditor : RawImageEditor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        EditorGUILayout.Space();
        var corner = serializedObject.FindProperty("m_Corner4");
        EditorGUILayout.PropertyField(corner);
        var alpha = serializedObject.FindProperty("m_Alpha");
        EditorGUILayout.PropertyField(alpha);
        var borderWidth = serializedObject.FindProperty("m_BorderWidth");
        EditorGUILayout.PropertyField(borderWidth);
        var borderColor = serializedObject.FindProperty("m_BorderColor");
        EditorGUILayout.PropertyField(borderColor);


        serializedObject.ApplyModifiedProperties();
    }
}

UIRoundConor_RawImageEditor请放在Editor文件目录下!

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值