unity 图片遮罩有锯齿_Unity | 实现在遮罩下面粒子特效的裁剪

4905eff2f597039f8dbe1a21b4c60f15.png

起因:

使用ScrollRect和遮罩实现滑动列表,列表中的物体带有粒子特效时,通过下面这张图片可以看出问题

25653ed7b8033a1f9c3db853b3ce29eb.png

粒子特效会透过遮罩显示出来。

解决方案:

修改shader + C#脚本来控制粒子特效显示区域

本例中粒子使用的shader是unity的Particle Add.shader

在官网下载shader源码,找到Particle Add.shade,修改后的shader如下:


Shader "Shader Forge/AdditiveClip" {

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

//新增,添加裁剪区域

_Area("Area", Vector) = (0,0,1,1)

}

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;

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);

//新增,通过矩阵把顶点从Object空间转换到世界空间

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

//新增,判断顶点坐标是否在裁剪框内

bool inArea = i.worldPos.x >= _Area.x && i.worldPos.x <= _Area.z && i.worldPos.y >= _Area.y && i.worldPos.y <= _Area.w;

//----end---

fixed4 col = 2.0 * 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

return inArea ? col : fixed4(0, 0, 0, 0);

//return col;

}

ENDCG

}

}

}

}

需要修改添加的地方,已经标记了注释,然后通过C#代码来动态设置显示的区域


C#代码:


public class EffectClip : MonoBehaviour

{

[SerializeField] RectTransform m_rectTrans;//裁剪范围

List<Material> materialList = new List<Material>();//需要修改的材质球列表

Transform canvas;//UI的根,Canvas

float halfWidth, halfHeight, canvasScale;

void Start()

{

canvas = GameObject.Find("Canvas").transform;

ScrollRect scroll = GetComponentInParent<ScrollRect>();

m_rectTrans = scroll.GetComponent<RectTransform>();

//获取所有需要修改shader的material,并替换shader

var particleSystems = GetComponentsInChildren<ParticleSystem>();

for (int i = 0, j = particleSystems.Length; i < j; i++)

{

var ps = particleSystems[i];

var mat = ps.GetComponent<Renderer>().material;

materialList.Add(mat);

}

var renders = GetComponentsInChildren<Image>();

for (int i = 0, j = renders.Length; i < j; i++)

{

var ps = renders[i];

var mat = ps.material;

materialList.Add(mat);

}

//获取UI的scale,容器的宽高的一半的值

canvasScale = canvas.localScale.x;

halfWidth = m_rectTrans.sizeDelta.x * 0.5f * canvasScale;

halfHeight = m_rectTrans.sizeDelta.y * 0.5f * canvasScale;

//修改shader的_Area值

Vector4 area = CalculateArea(m_rectTrans.position);

for (int i = 0, len = materialList.Count; i < len; i++)

{

materialList[i].SetVector("_Area", area);

}

}

//计算容器在世界坐标的Vector4,xz为左右边界的值,yw为下上边界值

Vector4 CalculateArea(Vector3 position)

{

return new Vector4()

{

x = position.x - halfWidth,

y = position.y - halfHeight,

z = position.x + halfWidth,

w = position.y + halfHeight

};

}

}


PS:借鉴了网上方法,当作笔记记录下来,加深印象

用法

把C#脚本挂到需要裁剪的粒子特效物体上面

如果成功的话如下图:

a10766698d4c2532c763feae85e09f6e.png

总结:

由于不同项目使用的粒子特效shader不同,但修改的方法大同小异,可以把上面的添加到shader的代码添加到你们项目使用的shader中去。

通过判断顶点是否在显示区域内,来裁剪不必要渲染的点,实现粒子特效在UI上面的裁剪。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值