效果如图
直接上代码和shader
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ParticleClippingController : MonoBehaviour
{
public RectMask2D rectMask;
private List<Material> rtlist = new List<Material>();
void Start()
{
var list = transform.GetComponentsInChildren<ParticleSystemRenderer>();
foreach (var renderer in list)
{
rtlist.Add(renderer.material);
}
}
void LateUpdate()
{
// 获取RectMask2D的世界空间裁剪区域
Vector3[] corners = new Vector3[4];
rectMask.rectTransform.GetWorldCorners(corners);
Vector4 clipRect = new Vector4(corners[0].x, corners[0].y, corners[2].x, corners[2].y);
// 将裁剪区域传递给材质
foreach (var mat in rtlist)
{
mat.SetVector("_ClipRect", clipRect);
}
}
}
// 定义一个名为 "Custom/ParticleClipping" 的着色器
Shader "Custom/ParticleClipping" {
// 定义着色器的属性,这些属性可以在材质面板中进行调整
Properties {
// _MainTex 是一个2D纹理属性,用于指定粒子的纹理
// "Particle Texture" 是该属性在材质面板中显示的名称
// "white" 是默认纹理,当没有指定纹理时使用白色纹理
_MainTex ("Particle Texture", 2D) = "white" {}
// _ClipRect 是一个四维向量属性,用于指定裁剪区域
// "Clip Rect" 是该属性在材质面板中显示的名称
// (0,0,1,1) 是默认的裁剪区域
_ClipRect ("Clip Rect", Vector) = (0,0,1,1)
}
// 子着色器,Unity会根据硬件和渲染需求选择合适的子着色器进行渲染
SubShader {
// 标签,用于指定渲染类型和渲染队列
// "RenderType"="Transparent" 表示这是一个透明渲染类型的着色器
// "Queue"="Transparent" 表示该着色器在透明渲染队列中进行渲染
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
// 细节级别,用于控制在不同硬件上的渲染质量
LOD 100
// 渲染通道,一个子着色器可以包含多个渲染通道
Pass {
// 混合模式,SrcAlpha 表示源颜色的透明度,OneMinusSrcAlpha 表示目标颜色的反透明度
// 这种混合模式用于实现透明效果
Blend SrcAlpha OneMinusSrcAlpha
// 关闭深度写入,因为透明物体通常不需要进行深度写入
ZWrite Off
// 开始CG编程块,用于编写顶点和片段着色器代码
CGPROGRAM
// 声明顶点着色器函数名为 vert
#pragma vertex vert
// 声明片段着色器函数名为 frag
#pragma fragment frag
// 包含Unity的常用CG库,提供了一些常用的函数和变量
#include "UnityCG.cginc"
// 定义输入结构体 appdata,用于存储从模型顶点数据传递过来的信息
struct appdata {
// POSITION 语义表示顶点的位置信息
float4 vertex : POSITION;
// TEXCOORD0 语义表示第一组纹理坐标
float2 uv : TEXCOORD0;
};
// 定义顶点到片段着色器的传递结构体 v2f,用于在顶点和片段着色器之间传递数据
struct v2f {
// 传递纹理坐标
float2 uv : TEXCOORD0;
// SV_POSITION 语义表示裁剪空间中的顶点位置
float4 vertex : SV_POSITION;
// 传递顶点的世界空间位置
float4 worldPos : TEXCOORD1;
};
// 声明纹理采样器,用于采样 _MainTex 纹理
sampler2D _MainTex;
// 纹理的缩放和平移参数
float4 _MainTex_ST;
// 裁剪区域的四维向量
float4 _ClipRect;
// 顶点着色器函数,将输入的顶点数据转换为裁剪空间中的顶点位置,并传递相关数据到片段着色器
v2f vert (appdata v) {
v2f o;
// 将顶点从模型空间转换到裁剪空间
o.vertex = UnityObjectToClipPos(v.vertex);
// 计算纹理坐标,考虑纹理的缩放和平移
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// 将顶点从模型空间转换到世界空间
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
// 片段着色器函数,根据输入的顶点数据计算每个像素的颜色
fixed4 frag (v2f i) : SV_Target {
// 判断粒子是否在裁剪区域内
// 如果粒子的世界空间 x 坐标小于裁剪区域的左边界,或者大于右边界
// 或者粒子的世界空间 y 坐标小于裁剪区域的下边界,或者大于上边界
if (i.worldPos.x < _ClipRect.x || i.worldPos.x > _ClipRect.z ||
i.worldPos.y < _ClipRect.y || i.worldPos.y > _ClipRect.w) {
// 如果不在裁剪区域内,则丢弃该像素,不进行渲染
discard;
}
// 从纹理中采样颜色
fixed4 col = tex2D(_MainTex, i.uv);
// 返回采样得到的颜色
return col;
}
// 结束CG编程块
ENDCG
}
}
// 回退着色器,当当前子着色器不支持时,使用 "Diffuse" 着色器进行渲染
FallBack "Diffuse"
}
使用方法
1.创建新材质,挂上上述shader,指定纹理
2.ScrollView挂上C#代码,遮罩采用RectMask2D
有其它更好、更方便的办法,可以提供一下,谢谢
顺便提一下,粒子在UI之间层级的控制,修改sort in layer即可,(canvas需要相机模式),可直接写成脚本控制