UGUI和Spine、ParticleSystem遮罩

         使用动效时,有时会需要使特定组件外的动效不显示,比如粒子特效只显示在特定区域或当列表中的特效移出显示区域时不显示动效,这时就需要使用遮罩,但是简单的使用Mask对动效是不起作用的。针对Spine动效和ParticleSystem动效需要分开处理。

一、在Spine动效中,只需要把Material的Shader从Spine/Skeleton改成Spine/SkeletonGraphic即可,如下图:

=>,这样拖动列表时,动效就会被遮住了。

2、在ParticleSystem动效中,需要使用代码来屏蔽。

public class ContentMask : MonoBehaviour
{
    private HashSet<ParticleSystemRenderer> renderers = new HashSet<ParticleSystemRenderer>();
    private MaterialPropertyBlock propBlock;

    private bool isDirty;
    
    void Awake()
    {
        propBlock = new MaterialPropertyBlock();
    }

    public void Add(ParticleSystemRenderer[] renders)
    {
        foreach (var render in renders)
        {
            this.renderers.Add(render);
            isDirty = true;
        }
    }

    public void Remove(ParticleSystemRenderer[] renders)
    {
        foreach (var render in renders)
        {
            this.renderers.Remove(render);
            isDirty = true;
        }
    }

    private IEnumerator Start()
    {
        yield return new WaitForEndOfFrame();
        SetClip();
    }

    private void Update()
    {
        if (isDirty)
        {
            isDirty = false;
            SetClip();
        }
    }

    protected void OnRectTransformDimensionsChange()
    {
        if (!isActiveAndEnabled) return;

        SetClip();//重新再给材质赋裁切参数
    }

    private void SetClip()
    {
        if(propBlock == null){
            propBlock = new MaterialPropertyBlock();
        }
        Camera canvasCamera = GetComponentInParent<Canvas>()?.rootCanvas?.worldCamera;
        if (canvasCamera == null) return;

        Vector3[] corners = new Vector3[4];
        RectTransform rectTransform = transform as RectTransform;
        rectTransform.GetWorldCorners(corners);
        corners[0] = canvasCamera.WorldToScreenPoint(corners[0]);
        corners[2] = canvasCamera.WorldToScreenPoint(corners[2]);
        float minX = corners[0].x;
        float minY = corners[0].y;
        float maxX = corners[2].x;
        float maxY = corners[2].y;

        propBlock.SetVector("_ClipRect", new Vector4(minX, minY, maxX, maxY));
        foreach (var renderer in renderers)
        {
            renderer.SetPropertyBlock(propBlock);
        }
    }

    public void Refresh()
    {
        isDirty = true;
    }
}

将上面的脚本挂到遮罩上(列表要挂在Viewport上)。

public class ParticleMask : MonoBehaviour
{
    private const string additiveShaderName = "Legacy Shaders/Particles/Additive";
    private const string alphaBlendedShaderName = "Legacy Shaders/Particles/Alpha Blended";
    private const string clipAdditiveShaderName = "Custom/Particles/ClipAdditive";
    private const string clipAlphaBlendedShaderName = "Custom/Particles/ClipAlphaBlended";

    static class ClipShaders
    {
        public readonly static Shader Additive = Shader.Find(clipAdditiveShaderName);
        public readonly static Shader AlphaBlended = Shader.Find(clipAlphaBlendedShaderName);
    }

    private ContentMask particleMask;
    private ParticleSystemRenderer[] renders;
    private List<Material> materials = new List<Material>();

    private bool isActive = false;

    private void Awake()
    {
        particleMask = GetComponentInParent<ContentMask>();
        isActive = particleMask != null;
        enabled = isActive;

        if (isActive)
        {
            renders = GetComponentsInChildren<ParticleSystemRenderer>(true);
            foreach (var psr in renders)
            {
                for (int i = 0; i < psr.sharedMaterials.Length; i++)
                {
                    if (psr.sharedMaterials[i] == null) continue;
                    switch (psr.sharedMaterials[i].shader.name)
                    {
                        case additiveShaderName:
                            psr.materials[i].shader = ClipShaders.Additive;
                            materials.Add(psr.materials[i]);
                            break;
                        case alphaBlendedShaderName:
                            psr.materials[i].shader = ClipShaders.AlphaBlended;
                            materials.Add(psr.materials[i]);
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    }

    private void OnEnable()
    {
        if (!isActive) return;

        particleMask.Add(renders);
    }

    private void OnDisable()
    {
        if (!isActive) return;

        particleMask.Remove(renders);
    }

    private void OnDestroy()
    {
        foreach (var material in materials)
        {
            Destroy(material);
        }
    }
}

将上面的脚本挂在粒子上。还需要加一个Shader脚本。

Shader "Custom/Particles/ClipAdditive"
{
	Properties{
		_TintColor("Tint Color", Color) = (0.5,0.5,0.5,0.5)
		_MainTex("Particle Texture", 2D) = "white" {}
		_InvFade("Soft Particles Factor", Range(0.01,3.0)) = 1.0

		//-------------------add----------------------
		_ClipRect("ClipRect", Vector) = (-10,-10,10,10)
		//-------------------add----------------------
	}

	Category{
		Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" }
		Blend SrcAlpha One
		ColorMask RGB
		Cull Off Lighting Off ZWrite Off

		SubShader{
			Pass{

				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma target 2.0
				#pragma multi_compile_particles
				#pragma multi_compile_fog

				#include "UnityCG.cginc"

				sampler2D _MainTex;
				fixed4 _TintColor;

				//-------------------add----------------------
				float4 _ClipRect;
				//-------------------add----------------------

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

				struct v2f {
					float4 vertex : SV_POSITION;
					fixed4 color : COLOR;
					float2 texcoord : TEXCOORD0;
					UNITY_FOG_COORDS(1)
					//#ifdef SOFTPARTICLES_ON
					float4 projPos : TEXCOORD2;
					//#endif
					//-------------------add----------------------
					float2 projPosScreenSize : TEXCOORD3;
					//-------------------add----------------------
					UNITY_VERTEX_OUTPUT_STEREO
				};

				float4 _MainTex_ST;

				v2f vert(appdata_t v)
				{
					v2f o;

					UNITY_SETUP_INSTANCE_ID(v);
					UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
					o.vertex = UnityObjectToClipPos(v.vertex);
					//#ifdef SOFTPARTICLES_ON
					o.projPos = ComputeScreenPos(o.vertex);
					//-------------------add----------------------
					o.projPosScreenSize.xy = (o.projPos.xy / o.projPos.w) * _ScreenParams.xy;
					#ifdef SOFTPARTICLES_ON
						//-------------------add----------------------
						COMPUTE_EYEDEPTH(o.projPos.z);
					#endif
					o.color = v.color;
					o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
					UNITY_TRANSFER_FOG(o,o.vertex);
					return o;
				}

				UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
				float _InvFade;

				fixed4 frag(v2f i) : SV_Target
				{
					#ifdef SOFTPARTICLES_ON
						float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
						float partZ = i.projPos.z;
						float fade = saturate(_InvFade * (sceneZ - partZ));
						i.color.a *= fade;
					#endif

					fixed4 col = 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord);
					col.a = saturate(col.a); // alpha should not have double-brightness applied to it, but we can't fix that legacy behavior without breaking everyone's effects, so instead clamp the output to get sensible HDR behavior (case 967476)

					UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(0,0,0,0)); // fog towards black due to our blend mode
					
					//-------------------add----------------------
					float2 inside = step(_ClipRect.xy, i.projPosScreenSize.xy) * step(i.projPosScreenSize.xy, _ClipRect.zw);
					col.a *= inside.x * inside.y;
					//-------------------add----------------------

					return col;
				}
				ENDCG
			}
		}
	}
	FallBack "Diffuse"
}
Shader "Custom/Particles/ClipAlphaBlended"
{
	Properties{
		_TintColor("Tint Color", Color) = (0.5,0.5,0.5,0.5)
		_MainTex("Particle Texture", 2D) = "white" {}
		_InvFade("Soft Particles Factor", Range(0.01,3.0)) = 1.0

		//-------------------add----------------------
		_ClipRect("ClipRect", Vector) = (-10,-10,10,10)
		//-------------------add----------------------
	}

	Category{
		Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" }
		Blend SrcAlpha OneMinusSrcAlpha
		ColorMask RGB
		Cull Off Lighting Off ZWrite Off

		SubShader{
			Pass{

				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma target 2.0
				#pragma multi_compile_particles
				#pragma multi_compile_fog

				#include "UnityCG.cginc"
				
				sampler2D _MainTex;
				fixed4 _TintColor;

				//-------------------add----------------------
				float4 _ClipRect;
				//-------------------add----------------------

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

				struct v2f {
					float4 vertex : SV_POSITION;
					fixed4 color : COLOR;
					float2 texcoord : TEXCOORD0;
					UNITY_FOG_COORDS(1)
					//#ifdef SOFTPARTICLES_ON
					float4 projPos : TEXCOORD2;
					//#endif
					//-------------------add----------------------
					float2 projPosScreenSize : TEXCOORD3;
					//-------------------add----------------------
					UNITY_VERTEX_OUTPUT_STEREO
				};

				float4 _MainTex_ST;

				v2f vert(appdata_t v)
				{
					v2f o;

					UNITY_SETUP_INSTANCE_ID(v);
					UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
					o.vertex = UnityObjectToClipPos(v.vertex);
					//#ifdef SOFTPARTICLES_ON
					o.projPos = ComputeScreenPos(o.vertex);
					//-------------------add----------------------
					o.projPosScreenSize.xy = (o.projPos.xy / o.projPos.w) * _ScreenParams.xy;
					#ifdef SOFTPARTICLES_ON
						//-------------------add----------------------
						COMPUTE_EYEDEPTH(o.projPos.z);
					#endif
					o.color = v.color * _TintColor;
					o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
					UNITY_TRANSFER_FOG(o,o.vertex);
					return o;
				}

				UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
				float _InvFade;

				fixed4 frag(v2f i) : SV_Target
				{
					#ifdef SOFTPARTICLES_ON
						float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
						float partZ = i.projPos.z;
						float fade = saturate(_InvFade * (sceneZ - partZ));
						i.color.a *= fade;
					#endif

					fixed4 col = 2.0f * i.color * tex2D(_MainTex, i.texcoord);
					col.a = saturate(col.a); // alpha should not have double-brightness applied to it, but we can't fix that legacy behavior without breaking everyone's effects, so instead clamp the output to get sensible HDR behavior (case 967476)

					UNITY_APPLY_FOG(i.fogCoord, col);

					//-------------------add----------------------
					float2 inside = step(_ClipRect.xy, i.projPosScreenSize.xy) * step(i.projPosScreenSize.xy, _ClipRect.zw);
					col.a *= inside.x * inside.y;
					//-------------------add----------------------

					return col;
				}
				ENDCG
			}
		}
	}
	FallBack "Diffuse"
}

然后粒子的Shader需要设置一下

或者,这样遮罩才会生效。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值