UGUI 列表边缘羽化

刚刚发现这玩意unity 的Rect Mask 2D已经内置了,真难受。
在这里插入图片描述
使用Rect Mask2D不需要一个image组件,性能更好。

在这里插入图片描述
不过TextMeshPro不受rectMask2d的soft影响,效果不好。
在这里插入图片描述
其实就是SoftMask里面的一个功能,不过SoftMask除了这个功能还有一堆其他的,而且Softmask的实现还非常复杂,我自己实现了一个,就几行代码,效果跟softmask差不多的。
实现原理:
以mask为终点边界假设20像素范围内,求出UI上的那个点到边界终点的距离,跟20比一下,来控制这个点或者像素的透明度,边界处和超过边界的为0.

两种实现方式:
1.不用shader,先重写unity的Image类,实现一个自己的可以增加顶点的Image类, 这里要能看懂unity image类源码的slice和simple两种方式是怎么绘制三角形把图形画出来的,比较麻烦,然后在写个BaseMeshEffect类,在重写ModifyMesh方式的时候,检测图形是否已经接触边界了,对接触边界的图形,增加他的顶点数量,非边界元素不加顶点,然后根据这个顶点离边界的距离算出一个alpha值来设置顶点的颜色。text或者textmeshpro是不会走modifyMesh的,所以这个可能要手动在lateupdate里面这种什么的调用控制。
这个代码就不公布了,非主流做法,做了别人反而会问你为什么不用shader做,用shader是比增加顶点性能要好的。
2. 使用shader,传递能把片元模型坐标转化成mask空间下坐标的矩阵过去,同理计算某个片元离边界的距离算出一个alpha值就好,我不懂shader,不过这个东西,不需要懂啥。

Shader "UI/UI-SoftMask"
{
	Properties
	{
		[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
		_Color("Tint", Color) = (1,1,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

		[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
	}

		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
			{
				Name "Default"
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma target 2.0

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

				#pragma multi_compile __ UNITY_UI_CLIP_RECT
				#pragma multi_compile __ UNITY_UI_ALPHACLIP

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

				struct v2f
				{
					float4 vertex   : SV_POSITION;
					fixed4 color : COLOR;
					float2 texcoord  : TEXCOORD0;
					float4 worldPosition : TEXCOORD1;
					UNITY_VERTEX_OUTPUT_STEREO
				};

				sampler2D _MainTex;
				fixed4 _Color;
				fixed4 _TextureSampleAdd;
				float4 _ClipRect;
				float4 _MainTex_ST;

				float4 _SoftMask_Rect;
				float4x4 _SoftMask_WorldToMask;
				float _Scope;

				v2f vert(appdata_t v)
				{
					v2f OUT;
					UNITY_SETUP_INSTANCE_ID(v);
					UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
					OUT.worldPosition = v.vertex;
					OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

					OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

					OUT.color = v.color * _Color;
					return OUT;
				}

				fixed4 frag(v2f IN) : SV_Target
				{
					half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

					#ifdef UNITY_UI_CLIP_RECT
					color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
					#endif

					// 边缘羽化
					float2 maskPosition = mul(_SoftMask_WorldToMask, IN.worldPosition);
					if (maskPosition.y < 0)
					{
						maskPosition.y *= -1;
					}
					color.a *= lerp(0, 1, saturate((_SoftMask_Rect.w - maskPosition.y) / _Scope));
#ifdef UNITY_UI_ALPHACLIP
					clip(color.a - 0.001);
#endif	
					return color;
				}
			ENDCG
			}
		}
}

首先把image本身的shader源码复制过去,最后frag里面加一段计算就完了。

  [ExecuteInEditMode]
    [RequireComponent(typeof(RectTransform))]
    public class SoftMask : UIBehaviour
    {
        public float scope=10;
        Canvas _canvas;
        Canvas canvas
        {
            get { return _canvas ? _canvas : (_canvas = NearestEnabledCanvas()); }
        }
        Canvas NearestEnabledCanvas()
        {
            var canvases = GetComponentsInParent<Canvas>(false);
            for (int i = 0; i < canvases.Length; ++i)
                if (canvases[i].isActiveAndEnabled)
                    return canvases[i];
            return null;
        }
        protected override void OnEnable()
        {
            base.OnEnable();
            SubscribeOnWillRenderCanvases();
            UpdateMaskable(transform);
        }
        protected override void OnDisable()
        {
            base.OnDisable();
            UnsubscribeFromWillRenderCanvases();
        }
        void SubscribeOnWillRenderCanvases()
        {
            Canvas.willRenderCanvases += OnWillRenderCanvases;
        }

        void UnsubscribeFromWillRenderCanvases()
        {
            Canvas.willRenderCanvases -= OnWillRenderCanvases;
        }
        void OnWillRenderCanvases()
        {
            UpdateMaskable(transform);
        }
        private void UpdateMaskable(Transform tran)
        {
            if (transform.childCount == 0)
                return;
            for (int i = 0; i < tran.childCount; i++)
            {
                Transform child = tran.GetChild(i);
                UpdateMaskable(child);
                if (child.GetComponent<CanvasRenderer>() != null)
                {
                    Material mat = child.GetComponent<CanvasRenderer>().GetMaterial(0);
                    if (mat != null)
                    {
                        Rect rect = ((RectTransform)transform).rect;
                        mat.SetVector(Shader.PropertyToID("_SoftMask_Rect"), new Vector4(rect.x, rect.y, rect.width/2, rect.height/2));
                        mat.SetMatrix(Shader.PropertyToID("_SoftMask_WorldToMask"), ((RectTransform)transform).worldToLocalMatrix * canvas.rootCanvas.transform.localToWorldMatrix);
                        mat.SetFloat(Shader.PropertyToID("_Scope"), scope);
                    }
                }
            }
        }
    }

@ 关注

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JustEasyCode

谢谢您

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值