1 媒介
基本诉求:想要一张图,表现时有圆角,且还能有boarder。图可以是纯色,也可以是图片。
在android,绘制如许的图非常简单,在xml声明一下就行。
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="#FFFFFFFF"/>
- <corners android:radius="25dp"/>
- <stroke android:width="3dp" android:color="#FFff0000"/>
- </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。
开根号浪费盘算,可以都加个平方。
即,从代码上,可以这么写:
- //左下角
- if (x < r && y < r)
- {
- arc_size = (x - r) * (x - r) + (y - r) * (y - r);
- if (arc_size > r * r) {
- color.a = 0;
- }
- }
复制代码 增补一点,是x的范围是0~width, y的范围是0 ~ height。
由于shader的极点坐标是归一化的,即0到1,以是需要归一化坐标,乘于view的宽和高。
- float x = IN.texcoord.x * width;
- float y = IN.texcoord.y * height;
复制代码 3 border的实现
边界,可以分8个地区,具体如下

分两种,一种是拐弯地区,一种是直线地区。
3.1 拐弯地区
以左下角为例子,我们放大地区看看。

分三部门,一个是镂空地区,一个是线地区,一个是线内地区。3者距离中央点不一样,以是我们还是可以根据某个点距离中央点(r, r) 来判定。
来,上代码:
- half4 color = IN.color;
- if (x < r && y < r)//左下角区域
- {
- //离中心点的距离(的平方)
- arc_size = (x - r) * (x - r) + (y - r) * (y - r);
- if (arc_size > r * r) {//超过圆的半径,则透明度为0
- color.a = 0;
- } else if (border_width > 0 && arc_size > (r - border_width) * (r - border_width)) {
- //要求有边线,且距离大于r - border_width, 为线的实际区域
- color = border_color;
- }
- }
复制代码 3.2 直线地区
直线就比较简单了,以下边中央的线为例子。地区范围是x > r && x < (width - r)。
如今,只需要包管y < border_width,则是线的现实地区,否则,为原始像素。
以是,代码如下
- half4 color = IN.color;
- //....
- if (border_width > 0) {
- //下边直线区域
- if (x > r && x < (width - r) && y < border_width) {
- color = border_color;
- }
- }
复制代码 4 完备shader代码
- Shader "Custom/UI/RoundConorNew"
- {
- Properties
- {
- [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
- _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
- _RoundedRadius("Rounded Radius", Range(0, 256)) = 64
- _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;
- };
-
- fixed4 _TextureSampleAdd;
- float4 _ClipRect;
- float _RoundedRadius;
- 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;
- }
- //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 r = _RoundedRadius;
- float arc_size = 0;
- //左下角
- 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;
- }
- }
- //左上角
- 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;
- }
- }
- //右下角
- 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;
- }
- }
- //右上角
- 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 > r && x < (width - r) && y < border_width) {
- color = border_color;
- }
- //上边直线区域
- if (x > r && x < (width - r) && (height - y) < border_width) {
- color = border_color;
- }
- //左边直线区域
- if (y > r && y < (height - r) && x < border_width) {
- color = border_color;
- }
- //右边直线区域
- if (y > r && y < (height - r) && x > (width - border_width)) {
- color = border_color;
- }
- }
- return color;
- }
- ENDCG
- }
- }
- }
复制代码 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
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |