UGUI - 解决粒子特效无法被遮罩遮住问题
今天UX要我给滚动列表上的item加上粒子特效,想着没问题啊。直接把特效挂在item上,但没有考虑到particle system的canvas order问题,导致出现了例子特效出现在窗口上方,特效并不能被mask遮盖掉的问题。
额外做了个简单的demo,scrollview做窗口
方案一:用图片遮盖特效
一开始想到用最简单最naive的方法是在窗口上下加个带canvas的图片, 通过调高它的层级把特效遮住,有点打补丁的意思。但!如果在多分辨率的屏幕适配的情况下,这个方法及其有可能被发现有问题,还是要谨慎使用哈
在底下加了个白色的图片,可以看出特效已经被遮盖住了
方案二:修改shader
不管窗口大小如何变都可以完美的遮住超出来的粒子特效,我们可以通过改shader的方式解决:给shader传scrollview的四个顶点的世界坐标,shader判断特效在框内的话则显示,反之则隐藏。
Step 1: 我的粒子特效挂的是unity内置的shader particle.addtive.shader, 先从unity官网上下载其源代码,注意用旧版本的比较好改,新版本的简化了很多,路径是builtin_shaders/DefaultResourcesExtra/Particle Add.shader. 把shader复制出一份并重命名Addtive.shader,修改的部分在注释上标出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | // Additive Particle shader that can be hidden if it is not in the canvas Shader "Particle/Additive" { 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 // Record the value of the border _Area ( "Area" , Vector) = (0,0,1,1) // 1 means clip on, 0 means clip off _IsClip ( "IsClip" , Int) = 0 } 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; float4 _Area; int _IsClip; 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 float2 worldPos : TEXCOORD3; 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); COMPUTE_EYEDEPTH(o.projPos.z); #endif o.color = v.color; o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xy; 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 // Check whether the vertex coordinates are in the clipping frame bool inArea = i.worldPos.x >= _Area.x && i.worldPos.x <= _Area.z && i.worldPos.y >= _Area.y && i.worldPos.y <= _Area.w; // If its position is in the clipping frame or the effect of cliping is off, return the original effect, otherwise it will be hidden return (!inArea && _IsClip == 1) ? fixed4(0, 0, 0, 0) : col; } ENDCG } } } } |
Step2: 先将将UI中的粒子特效的shader都改成修改过后的Addtive.shader,再写一个用来计算裁剪框的四个顶点的世界坐标并传到shader里的脚本VFXClip.cs,把其挂到相对应的特效上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | using System.Collections.Generic; using UnityEngine; namespace Demo { public class VFXClip : MonoBehaviour { private RectTransform rectTrans; // Mask transform private List<Material> materialList = new List<Material>(); private Transform canvas; private float halfWidth; private float halfHeight; private float canvasScale; void Awake() { var mask = this .transform.GetComponentInParent<UnityEngine.UI.Mask>(); if (mask != null ) { this .rectTrans = mask.gameObject.GetComponent<RectTransform>(); } } void Start() { if ( this .rectTrans == null ) { return ; } this .canvas = this .transform.GetComponentInParent<Canvas>().transform; var renders = this .transform.GetComponentsInChildren<ParticleSystemRenderer>(); for ( int i = 0, j = renders.Length; i < j; i++) { var render = renders[i]; var mat = render.material; this .materialList.Add(mat); } this .canvasScale = this .canvas.localScale.x; this .halfWidth = this .rectTrans.rect.width * 0.5f * this .canvasScale; this .halfHeight = this .rectTrans.rect.height * 0.5f * this .canvasScale; Vector4 area = this .CalculateArea( this .rectTrans.position); for ( int i = 0, len = this .materialList.Count; i < len; i++) { this .materialList[i].SetInt( "_IsClip" , 1); this .materialList[i].SetVector( "_Area" , area); } } private Vector4 CalculateArea(Vector3 position) { return new Vector4() { x = position.x - this .halfWidth, y = position.y - this .halfHeight, z = position.x + this .halfWidth, w = position.y + this .halfHeight }; } } } |
N.B. 要注意计算四个顶点的时候,viewport的pivot值会影响到中心点的位置, CalcaulateArea这个函数根据我scrollview所用的pivot来算
最终效果图:
__EOF__

本文链接:https://www.cnblogs.com/cancantrbl/p/15111045.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码