圆角描边矩形

UI总是会有很多奇奇怪怪的圆角矩形,虽然要在UIToolkit中实现是很简单的事情,但目前还是不太成熟,还是要用UGUI。在UGUI中要实现大致有三种方法,这里用Shader来实现一下。

单纯圆角图片

这是最麻烦且没必要的一种,针对不同分辨率和曲率要UI给出不同的图片,效率很低

9切片

9SliceSprites
把图片分割成9个部分,每个部分有不同的UV平铺与重复的方式,但是要实现像素级别的点对点,就需要准备不同分辨率的切片圆角矩形,也很麻烦

Shader实现

直接用SDF实现,一劳永逸。保证点对点的准确度,即使再搞什么圆角加描边也能快速实现。

UI shader

UI的shader与一般的差别不大,只是对一些命名有点小要求。

  • 传入的主贴图名称为_MainTex当然如果你不想用Image传入的就随便了。
  • 同时还会传入颜色float4 color : COLOR;
  • 默认的一些配置
    • ZClip True
    • ZTest Always
    • ZWrite Off
    • Cull Off
  • UGUI传入的顶点并不仅仅是一个面片,根据不同的设置,它会生成特定的网格传入GPU,而且UV分布也不一定是均匀的,所以要用特定的简单Sprite。

SDF

从shaderToy上抄一个

float sdRoundedBox(in float2 p, in float2 b, in float4 r)
{
    r.xy = (p.x > 0.0) ? r.xy : r.zw;
    r.x = (p.y > 0.0) ? r.x : r.y;
    float2 q = abs(p) - b + r.x;
    return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x;
}

Shader

最后贴一下Shader
圆角描边矩形

Shader "Unlit/UITest"
{
    Properties
    {
        _OutlineColor("Outline Color", Color) = (1,0,0,1)
        [PerRendererData] _MainTex("Base Map", 2D) = "white"
        _scale("Scale width/height", float) = 1.0
        _width("Outline Width", float) = 1.0
        _Radius("Radius", Vector) = (0.1, 0.1, 0.1, 0.1)
    }

    SubShader
    {
        Tags
        {
            "RenderType" = "Transparent"
            "RenderPipeline" = "UniversalPipeline"
            "Queue"="Transparent"
        }

        Pass
        {
            Name "Default"
            Blend SrcAlpha OneMinusSrcAlpha
            ZClip True
            ZTest Always
            ZWrite Off
            Cull Off

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
                float4 color : COLOR;
                float2 uv : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
                float4 color : COLOR;
                float2 uv : TEXCOORD0;
            };

            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);

            CBUFFER_START(UnityPerMaterial)
            float4 _MainTex_ST;
            float4 _OutlineColor;
            float _scale;
            float _width;
            float4 _Radius;
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.color = IN.color;

                OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex);
                return OUT;
            }

            float sdRoundedBox(in float2 p, in float2 b, in float4 r)
            {
                r.xy = (p.x > 0.0) ? r.xy : r.zw;
                r.x = (p.y > 0.0) ? r.x : r.y;
                float2 q = abs(p) - b + r.x;
                return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x;
            }

            float boxDist(float2 p, float2 size, float radius)
            {
                size -= float2(radius,radius);
                float2 d = abs(p) - size;
                return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                half4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv) * IN.color;

                float2 uv = (IN.uv * 2 - 1) ;
                uv.x *= _scale;

                float d = sdRoundedBox(uv, float2(_scale,1), _Radius);

                if (d > 0.0)
                    color = half4(0, 0, 0, 0);
                else if (d > - _width)
                    color = _OutlineColor;

                return color;
            }
            ENDHLSL
        }
    }
}

其他

这里的SDF可以简单替换其他各种奇奇怪怪的类型,比如参考这个链接distfunctions2d。再结合一些变化的参数就可以做出动画效果了。


圆角描边矩形
https://www.kuanmi.top/2023/01/02/UIShader/
作者
KuanMi
发布于
2023年1月3日
许可协议