Unity 圆角矩形Shader实现(支持长方形)(只写两行)

一、 效果与引言

在这里插入图片描述
相信很多小伙伴都会遇到做圆角矩形的需求,网上的shader还不明白是怎么实现的,甚至还有一部分是错误的,本文讲从原理到代码讲解圆角矩形shader的实现

二、 原理分析

想要实现一个圆角矩形,常见的是抽象成一个数学模型,如下图紫色区域,就是我们应该保留的区域,为了更准确的描述这个图形,我们在四个角创建四个相等的圆形。
在这里插入图片描述
因为控制每个像素的颜色主要是由片元着色器负责的,所以我们也通过Fragment Shader去实现这个效果,可以看到在这个函数里我们只能拿到 uv和vertex,所以我们根据uv坐标判定是否在上图的紫色区域,如果在则返回原本的颜色,如果不在返回完全透明的颜色。
(对每个像素都会执行一次frag函数获取真正渲染的颜色)

struct v2f
            {
    
    
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };
            
 fixed4 frag (v2f i) : SV_Target
 {
    
    
 	//....省略
 }

因为我们通过uv来判断是否是紫色区域,所以我们需要先了解uv坐标系
首先我们要了解UV坐标系,这并不是什么高深的数学概念,而是一个简单的道理。
首先,我们把水平方向定义为X轴,竖直方向为Y轴把图片左下角定义为(0,0),右上角定义为(1,1),其他的坐标以此类推

在这里插入图片描述

读者可能会产生疑惑,为什么xy两个轴长度不同,但是坐标却不相同?
笔者暂时只能这样描述:我们就如此定义一个坐标系,x轴和y轴单位长度不同

我们需要用户去调整四个圆形的半径 设为Radius,因为uv坐标系的xy单位长度不同,我们设Ratio = Height/Width,Ratio即为y轴单位长度与x轴单位长度之比。

float Radius 圆形半径
float Ratio Y单位长度/X单位长度

接下来我们在图上画四个圆形并作辅助线

在这里插入图片描述
通过观察,我们可以发现以下特征

1.只有在红色区域才有可能被舍弃
2.三个圆形R2,R3,R4对应红色区域的任意一个位置,都能在R1内找到等价位置
3. UV坐标转正常坐标的公式为 f (x ,y)= (uv.x , uv.y * Ratio)

在UV坐标内,我们无法通过x2 + y2 来计算长度,因为uv坐标的xy单位不同,所以我们通过上述的坐标转换公式来转化为相同的坐标系

我们需要按以下步骤进行处理

1.将白色区域坐标在左下角找到等价uv坐标,position = abs(step(0.5,uv) - uv)
在这里插入图片描述
2.判断等价uv是否在左下角可能舍弃区域,uv.x<Raduis && uv.y < Radius * Ratio
如果不在,则返回原色,如果在则进入下一步

在这里插入图片描述
3. 求圆心距 distance = [f(uv.x,uv.y) - f(Radius,Radius)]的长度
如果大于半径则返回fixed(0,0,0,0),否则返回原色

在这里插入图片描述

三、着色器代码

在shader内尽量不要使用if语句
在这里插入图片描述
以上来着自Hking_Auditore 大大的Shader入门书,通常我们用step和lerp等来代替if
step函数的逻辑可以等价为

step (a, x)
{
    
    
  if (a>x)  return 0;
  else return 1;
}

接下来的代码虽然看着多,实际上我们只写了两行
这就是这两行

 float2 p = abs(step(0.5,i.uv) - i.uv);
 fixed4 col =  tex2D(_MainTex, i.uv) * (step(_Radius,p.x) ||step( _Radius  ,p.y*_Ratio) || step(length(float2(p.x-_Radius,p.y*_Ratio-_Radius)),_Radius));

这是完整的着色器代码

Shader "Unlit/MyShader"
{
    
    
    Properties
    {
    
    
        _MainTex ("Texture", 2D) = "white" {
    
    }
        _Radius ("Radius",float) = 0
        _Ratio("Height/Width",float )= 1
    }
    SubShader
    {
    
    
        Tags {
    
     "RenderType"="Transparent" "Queue" = "Transparent"}
        LOD 100
        Blend SrcAlpha OneMinusSrcAlpha
        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
    
    
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Radius;
            float _Ratio;
            v2f vert (appdata v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
    
      
            	//坐标等价到左下角
                float2 p = abs(step(0.5,i.uv) - i.uv);
                //三个条件同时成立则乘0,否则乘1
                //1.在左下角  ,2.长度超过半径
                fixed4 col =  tex2D(_MainTex, i.uv) * (step(_Radius,p.x) ||step( _Radius  ,p.y*_Ratio) || step(length(float2(p.x-_Radius,p.y*_Ratio-_Radius)),_Radius));
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_46273241/article/details/128596588