工作中经常会用到滑动列表中使用一个特效。
首先在需要被遮挡的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
}
}
}