让粒子特效可以被RectMask2D正确遮挡

工作中经常会用到滑动列表中使用一个特效。

首先在需要被遮挡的ParticleSystem上挂载这个脚本

[RequireComponent(typeof(ParticleSystem))]
public class ParticleClippable : UIBehaviour, IClippable
{
    MaterialPropertyBlock props;
    private ParticleSystemRenderer particleSystemRenderer;
    private ParticleSystem _particleSystem;

    private new ParticleSystem particleSystem
    {
        get
        {
            if (_particleSystem == null)
            {
                _particleSystem = GetComponent<ParticleSystem>();
            }
            return _particleSystem;
        }
    }
    private RectTransform m_RectTransform;
    public RectTransform rectTransform
    {

        get
        {
            // The RectTransform is a required component that must not be destroyed. Based on this assumption, a
            // null-reference check is sufficient.
            if (ReferenceEquals(m_RectTransform, null))
            {
                m_RectTransform = GetComponent<RectTransform>();
            }
            return m_RectTransform;
        }

    }

    private RectMask2D m_ParentMask;
    protected override void OnEnable()
    {
        UpdateClipParent();
    }
    public void Cull(Rect clipRect, bool validRect)
    {
    }
    private void UpdateClipParent()
    {
        print("UpdateClipParent");
        // if the new parent is different OR is now inactive
        SetClipRect(Rect.zero, false);
        if (!gameObject.activeSelf)
        {
            return;
        }
        var newParent = MaskUtilities.GetRectMaskForClippable(this);
        if (m_ParentMask != null && (newParent != m_ParentMask || !newParent.IsActive()))
        {
            m_ParentMask.RemoveClippable(this);
        }
        // don't re-add it if the newparent is inactive
        if (newParent != null && newParent.IsActive())
            newParent.AddClippable(this);

        m_ParentMask = newParent;
    }
    public void RecalculateClipping()
    {
        UpdateClipParent();
    }
    public void SetClipRect(Rect value, bool validRect)
    {
        Material material = GetMaterial();
        if (!isActiveAndEnabled)
        {
            return;
        }
        if (material == null)
        {
            return;
        }
        if (!material.HasProperty("_UNITY_UI_CLIP_RECT"))
        {
            print("没有UNITY_UI_CLIP_RECT");
            return;
        }
        if (props == null)
        {
            props = new MaterialPropertyBlock();
        }
        float _UNITY_UI_CLIP_RECT = material.GetFloat("_UNITY_UI_CLIP_RECT");
        print(_UNITY_UI_CLIP_RECT);
        if (validRect&& _UNITY_UI_CLIP_RECT > 0)
        { 
            props.SetVector("_ClipRect", new Vector4(value.xMin, value.yMin, value.xMax, value.yMax));
            particleSystemRenderer.SetPropertyBlock(props);
        }
    }
    private Vector2 clipSoftness;
    public void SetClipSoftness(Vector2 _clipSoftness)
    {
        if (clipSoftness == _clipSoftness)
        {
            return;
        }
        clipSoftness = _clipSoftness;
    }
    private Material GetMaterial()
    {
        if (particleSystem == null)
        {
            return null;
        }
        if (particleSystemRenderer == null)
        {
            particleSystemRenderer = particleSystem.GetComponent<ParticleSystemRenderer>();
        }
        if (particleSystemRenderer == null)
        {
            return null;
        }
        return particleSystemRenderer.sharedMaterial;
    }
    protected override void OnTransformParentChanged()
    {
        UpdateClipParent();
    }
#if UNITY_EDITOR
    protected override void OnValidate()
    {
        UpdateClipParent();
    }
#endif
    protected override void OnDisable()
    {
        SetClipRect(Rect.zero, false);
    }
}

shader代码

Shader "NewUnlitShader"
{
   Properties
    {
        [Toggle(UNITY_UI_CLIP_RECT)] _UNITY_UI_CLIP_RECT("启用粒子遮罩", Float) = 0
    }
    SubShader
    {
        Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="Transparent"  "Queue"="Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        LOD 100

        Cull Off
        ZTest LEqual
        ZWrite Off

        Pass
        {
            Tags{"LightMode" = "UniversalForward"}

            HLSLPROGRAM
            #pragma prefer_hlslcc gles
            #pragma target 3.0

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #pragma shader_feature_local _ UNITY_UI_CLIP_RECT

            #pragma vertex ForwardVertex
            #pragma fragment ForwardFragment

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

            struct Varyings
            {
                 float2 uv : TEXCOORD0;
                float4 screenPos : TEXCOORD1;
                float4 vertex : SV_POSITION;
            };
            float4 _ClipRect;
            float _UNITY_UI_CLIP_RECT;
            //计算一个像素点是否在一个区域内
            float PixelIsRect(float2 Pixel,float4 Rect)
            {
                float SignX = saturate((Pixel.x - Rect.x) * (Pixel.x - Rect.z));
                float SignY = saturate((Pixel.y - Rect.y) * (Pixel.y - Rect.w));
                return saturate(SignX + SignY);
            }

            Varyings ForwardVertex(Attributes v)
            {
                Varyings o;
                o.vertex = TransformObjectToHClip(v.vertex);
                o.screenPos = ComputeScreenPos(o.vertex);
                return o;
            }   

            half4 ForwardFragment(Varyings i):SV_Target
            {
                float4 col = float4(1,0,0,1);
                #ifdef UNITY_UI_CLIP_RECT 
                    col = float4(0,0,1,1);
                    float2  ScreenPos=i.screenPos.xy / i.screenPos.w;
                    ScreenPos = (ScreenPos * 2 - 1) * _ScreenParams.xy * 0.5;
                    float IsRect= PixelIsRect(ScreenPos,_ClipRect);
                    col.a *= 1 - IsRect;
                #endif
                // apply fog
                return col;
            }
            ENDHLSL
        }
    }
}
  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值