[Unity学习教程] Unity shader 实现图片带圆角和边线border

[复制链接]
查看696 | 回复0 | 2023-8-23 11:37:23 | 显示全部楼层 |阅读模式 来自 中国北京
1 媒介

基本诉求:想要一张图,表现时有圆角,且还能有boarder。图可以是纯色,也可以是图片。
在android,绘制如许的图非常简单,在xml声明一下就行。
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android">
  3.     <solid android:color="#FFFFFFFF"/>
  4.     <corners android:radius="25dp"/>
  5.     <stroke android:width="3dp" android:color="#FFff0000"/>
  6. </shape>
复制代码
圆角一般设置一个半径radius,边线border一般设置线宽&颜色即可。

在Unity,就贫苦一些,要借助shader大概脚本实现,本文着重讨论一下用shader怎么实现。
2 圆角的实现


先研究左下角怎么抠掉。
当x < r 且 y < r,则可以分为上图的地区A和地区B,此中地区A是需要设置alpha = 0的地区。地区B则保持纹理颜色。
怎么找出地区A呢?
首先,圆角的中央点,坐标为(r, r),则这个地区内的恣意一点(x, y), 距离中央点的距离arc_size为

假如arc_size大于圆的半径r,则是在地区A,否则,在地区B。
开根号浪费盘算,可以都加个平方。
即,从代码上,可以这么写:
  1. //左下角
  2. if (x < r && y < r)
  3. {
  4.     arc_size = (x - r) * (x - r) + (y - r) * (y - r);
  5.     if (arc_size > r * r) {
  6.         color.a = 0;
  7.     }
  8. }
复制代码
增补一点,是x的范围是0~width, y的范围是0 ~ height。
由于shader的极点坐标是归一化的,即0到1,以是需要归一化坐标,乘于view的宽和高。
  1. float x = IN.texcoord.x * width;
  2. float y = IN.texcoord.y * height;
复制代码
3 border的实现

边界,可以分8个地区,具体如下

分两种,一种是拐弯地区,一种是直线地区。
3.1 拐弯地区

以左下角为例子,我们放大地区看看。

分三部门,一个是镂空地区,一个是线地区,一个是线内地区。3者距离中央点不一样,以是我们还是可以根据某个点距离中央点(r, r) 来判定。
来,上代码:
  1. half4 color = IN.color;
  2. if (x < r && y < r)//左下角区域
  3. {
  4.     //离中心点的距离(的平方)
  5.         arc_size = (x - r) * (x - r) + (y - r) * (y - r);
  6.         if (arc_size > r * r) {//超过圆的半径,则透明度为0
  7.                 color.a = 0;
  8.         } else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width)) {
  9.             //要求有边线,且距离大于r - border_width, 为线的实际区域
  10.                 color = border_color;
  11.         }   
  12. }
复制代码
3.2 直线地区

直线就比较简单了,以下边中央的线为例子。地区范围是x > r && x < (width - r)。
如今,只需要包管y < border_width,则是线的现实地区,否则,为原始像素。
以是,代码如下
  1. half4 color = IN.color;
  2. //....
  3. if (border_width > 0) {
  4.         //下边直线区域
  5.         if (x > r && x < (width - r) && y < border_width) {
  6.                 color = border_color;
  7.         }
  8. }
复制代码
4 完备shader代码

  1. Shader "Custom/UI/RoundConorNew"
  2. {
  3.     Properties
  4.     {
  5.         [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
  6.         _StencilComp("Stencil Comparison", Float) = 8
  7.         _Stencil("Stencil ID", Float) = 0
  8.         _StencilOp("Stencil Operation", Float) = 0
  9.         _StencilWriteMask("Stencil Write Mask", Float) = 255
  10.         _StencilReadMask("Stencil Read Mask", Float) = 255
  11.         _ColorMask("Color Mask", Float) = 15
  12.         _RoundedRadius("Rounded Radius", Range(0, 256)) = 64
  13.         _Width("View Width", Float) = 200
  14.         _Height("View Height", Float) = 200
  15.         _BorderWidth("Border Width", Float) = 1
  16.         _BorderColor("Boader Color", Color) = (1, 0, 0, 1)
  17.     }
  18.         SubShader
  19.         {
  20.             Tags
  21.             {
  22.                 "Queue" = "Transparent"
  23.                 "IgnoreProjector" = "True"
  24.                 "RenderType" = "Transparent"
  25.                 "PreviewType" = "Plane"
  26.                 "CanUseSpriteAtlas" = "True"
  27.             }
  28.             Stencil
  29.             {
  30.                 Ref[_Stencil]
  31.                 Comp[_StencilComp]
  32.                 Pass[_StencilOp]
  33.                 ReadMask[_StencilReadMask]
  34.                 WriteMask[_StencilWriteMask]
  35.             }
  36.             Cull Off
  37.             Lighting Off
  38.             ZWrite Off
  39.             ZTest[unity_GUIZTestMode]
  40.             Blend SrcAlpha OneMinusSrcAlpha
  41.             ColorMask[_ColorMask]
  42.             Pass
  43.             {
  44.                 CGPROGRAM
  45.     #pragma vertex vert
  46.     #pragma fragment frag
  47.     #include "UnityCG.cginc"
  48.     #include "UnityUI.cginc"
  49.     #pragma multi_compile __ UNITY_UI_ALPHACLIP
  50.                 struct appdata_t
  51.                 {
  52.     float4 vertex   :
  53.                     POSITION;
  54.     float4 color    :
  55.                     COLOR;
  56.     float2 texcoord :
  57.                     TEXCOORD0;
  58.                 };
  59.                 struct v2f
  60.                 {
  61.     float4 vertex   :
  62.                     SV_POSITION;
  63.     fixed4 color :
  64.                     COLOR;
  65.     half2 texcoord  :
  66.                     TEXCOORD0;
  67.     float4 worldPosition :
  68.                     TEXCOORD1;
  69.                 };
  70.                
  71.                 fixed4 _TextureSampleAdd;
  72.                 float4 _ClipRect;
  73.                 float _RoundedRadius;
  74.                 float _Width;
  75.                 float _Height;
  76.                 float _BorderWidth;
  77.                 float4 _BorderColor;
  78.                 float4 _MainTex_TexelSize;//纹理的大小,可能没有纹理,只有顶点颜色
  79.                 v2f vert(appdata_t IN)
  80.                 {
  81.                     v2f OUT;
  82.                     OUT.worldPosition = IN.vertex;
  83.                     OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
  84.                     OUT.texcoord = IN.texcoord;
  85.                     OUT.color = IN.color;
  86.                     return OUT;
  87.                 }
  88.                 sampler2D _MainTex;
  89.                 fixed4 frag(v2f IN) : SV_Target
  90.                 {
  91.                     half4 color = IN.color;
  92.                     if (_MainTex_TexelSize.z > 0) {
  93.                         //有纹理,则颜色从纹理读取, 并叠加顶点颜色
  94.                         color = (tex2D(_MainTex, IN.texcoord)) * IN.color;
  95.                     }
  96.                     //float width = _MainTex_TexelSize.z;
  97.                     //float height = _MainTex_TexelSize.w;
  98.                     float width = _Width;
  99.                     float height = _Height;
  100.                     if (width <= 0 && _MainTex_TexelSize.z > 0)
  101.                     {
  102.                         //如果没定义宽度,而纹理又定义了宽度,则从纹理宽度读取
  103.                         width = _MainTex_TexelSize.z;
  104.                     }
  105.                     if (height <= 0 && _MainTex_TexelSize.w > 0)
  106.                     {
  107.                         //同上
  108.                         height = _MainTex_TexelSize.w;
  109.                     }
  110.                     float border_width = _BorderWidth;
  111.                     half4 border_color = _BorderColor;
  112.                     float x = IN.texcoord.x * width;
  113.                     float y = IN.texcoord.y * height;
  114.                     float r = _RoundedRadius;
  115.                     float arc_size = 0;
  116.                     //左下角
  117.                     if (x < r && y < r)
  118.                     {
  119.                         arc_size = (x - r) * (x - r) + (y - r) * (y - r);
  120.                         if (arc_size > r * r) {
  121.                             color.a = 0;
  122.                         } else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width)) {
  123.                             color = border_color;
  124.                         }   
  125.                     }
  126.                     //左上角
  127.                     if (x < r && y >(height - r))
  128.                     {
  129.                         arc_size = (x - r) * (x - r) + (y - (height - r)) * (y - (height - r));
  130.                         if (arc_size > r * r) {
  131.                             color.a = 0;
  132.                         }
  133.                         else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width)) {
  134.                             color = border_color;
  135.                         }
  136.                     }
  137.                     //右下角
  138.                     if (x > (width - r) && y < r)
  139.                     {
  140.                         arc_size = (x - (width - r)) * (x - (width - r)) + (y - r) * (y - r);
  141.                         if (arc_size > r * r) {
  142.                             color.a = 0;
  143.                         }
  144.                         else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width)) {
  145.                             color = border_color;
  146.                         }
  147.                     }
  148.                     //右上角
  149.                     if (x > (width - r) && y > (height - r))
  150.                     {
  151.                         arc_size = (x - (width - r)) * (x - (width - r)) + (y - (height - r)) * (y - (height - r));
  152.                         if (arc_size > r * r) {
  153.                             color.a = 0;
  154.                         } else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width)) {
  155.                             color = border_color;
  156.                         }
  157.                     }
  158.                     if (border_width > 0) {
  159.                         //下边直线区域
  160.                         if (x > r && x < (width - r) && y < border_width) {
  161.                             color = border_color;
  162.                         }
  163.                         //上边直线区域
  164.                         if (x > r && x < (width - r) && (height - y) < border_width) {
  165.                             color = border_color;
  166.                         }
  167.                         //左边直线区域
  168.                         if (y > r && y < (height - r) && x < border_width) {
  169.                             color = border_color;
  170.                         }
  171.                         //右边直线区域
  172.                         if (y > r && y < (height - r) && x > (width - border_width)) {
  173.                             color = border_color;
  174.                         }
  175.                     }
  176.                     return color;
  177.                 }
  178.                 ENDCG
  179.             }
  180.         }
  181. }
复制代码
5 shader利用方式

5.1 创建shader

在Unity 开辟环境中,Assets的某个子目次下,右键,Create - Shader - Unlit Shader,将创建一个不带光照的shader文件,但无所谓,我们都要删除。双击打开,把上面第4节的代码黏贴覆盖。
5.2 创建材质Material

右键,Create - Material,新建一个材质。
然后,材质的shader选择为Custom/UI/RoundConorNew,也就是5.1新建的shader名字。
接着,调解参数,重要有5个参数需要关心:

Rounded Radius就是圆角的半径。
View Width & View Height 是目的UI的宽高。
Border Width是边线宽度。
Border Color是边线颜色。
假如不要边线,把Border Width设置为0即可。
5.3 应用

在Unity的中,新建一个Image,然后把材质选为上面5.2新建的材质即可。


来源:https://blog.csdn.net/newchenxf/article/details/125811641
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则