Shader相关Mask裁切UI粒子特效或者3D模型
转载自:http://www.xuanyusong.com/archives/3518
原理就是把Mask的裁切区域传给粒子特效Shader,当超出这个区域那么直接让它完全透明即可。粒子特效的源生shader大家可以去unity官网下载,我在这里把需要修改的地方标注给大家。
//add 注释中的内容就是我做修改的地方。
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 102 103 104 |
Shader "Particles/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
//-------------------add---------------------- _MinX ("Min X", Float) = -10 _MaxX ("Max X", Float) = 10 _MinY ("Min Y", Float) = -10 _MaxY ("Max Y", Float) = 10 //-------------------add----------------------
}
Category { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } Blend SrcAlpha One AlphaTest Greater .01 ColorMask RGB Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
SubShader { Pass {
CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_particles
#include "UnityCG.cginc"
sampler2D _MainTex; fixed4 _TintColor; //-------------------add---------------------- float _MinX; float _MaxX; float _MinY; float _MaxY; //-------------------add----------------------
struct appdata_t { float4 vertex : POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; };
struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; #ifdef SOFTPARTICLES_ON float4 projPos : TEXCOORD1; #endif //-------------------add---------------------- float3 vpos : TEXCOORD2; //-------------------add---------------------- };
float4 _MainTex_ST;
v2f vert (appdata_t v) { v2f o; //-------------------add---------------------- o.vpos = v.vertex.xyz; //-------------------add---------------------- o.vertex = mul(UNITY_MATRIX_MVP, 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); return o; }
sampler2D_float _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
//-------------------add---------------------- fixed4 c =2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord); c.a *= (i.vpos.x >= _MinX ); c.a *= (i.vpos.x <= _MaxX); c.a *= (i.vpos.y >= _MinY); c.a *= (i.vpos.y <= _MaxY); c.rgb *= c.a; return c; //-------------------add---------------------- } ENDCG } } } } |
然后是自己写了个类继承Mask。把Mask的区域传给shader。
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class MyMask :Mask
{
protected override void Start ()
{
base.Start ();
int width = Screen.width;
int height = Screen.height;
int designWidth = 960;//开发时分辨率宽
int designHeight = 640;//开发时分辨率高
float s1 = (float)designWidth / (float)designHeight;
float s2 = (float)width / (float)height;
//目标分辨率小于 960X640的
需要计算缩放比例
float contentScale =1f;
if(s1 > s2) {
contentScale = s1/s2;
}
Canvas canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
Vector2 pos;
if(RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, transform.position, canvas.camera, out pos)){
ParticleSystem [] particlesSystems = transform.GetComponentsInChildren<ParticleSystem>();
RectTransform rectTransform = transform as RectTransform;
float minX,minY,maxX,maxY;
minX = rectTransform.rect.x + pos.x;
minY = rectTransform.rect.y+ pos.y;
maxX = minX + rectTransform.rect.width ;
maxY = minY + rectTransform.rect.height;
//这里 100 是因为ugui默认的缩放比例是100 你也可以去改这个值,但是我觉得最好别改。
foreach(ParticleSystem particleSystem in particlesSystems)
{
particleSystem.renderer.sharedMaterial.SetFloat("_MinX",minX/100/contentScale);
particleSystem.renderer.sharedMaterial.SetFloat("_MinY",minY/100/contentScale);
particleSystem.renderer.sharedMaterial.SetFloat("_MaxX",maxX/100/contentScale);
particleSystem.renderer.sharedMaterial.SetFloat("_MaxY",maxY/100/contentScale);
}
}
}
}
上面这段代码写的不太好,有一个更好的办法来取Mask的裁切区域。
通过GetWorlCornets来确定裁切的区域
1 2 3 4 5 6 7 |
Vector3[] corners = new Vector3[4]; RectTransform rectTransform = transform as RectTransform; rectTransform.GetWorldCorners (corners); minX = corners [0].x; minY = corners [0].y; maxX = corners [2].x; maxY = corners [2].y; |
然后在把裁切的区域传到shader中。
1 2 3 4 5 |
Material m = GetMaterial (renderer); m.SetFloat("_MinX",minX); m.SetFloat("_MinY",minY); m.SetFloat("_MaxX",maxX); m.SetFloat("_MaxY",maxY); |
为了做到不影响美术,所以美术开发特效的时候还是用以前的shader。程序在运行中对它进行更换,这样可以无缝进行切换。
如果运行时裁切区域发生变化, 可以重写OnRectTransformDimensionsChange()方法来重新给材质赋新的裁切区域
1 2 3 4 5 |
protected override void OnRectTransformDimensionsChange () { base.OnRectTransformDimensionsChange (); Change ();//重新再给材质赋裁切参数 } |
OK,如下图所示,把粒子特效直接挂在Mask下面, 就可以进行裁切了。。
在说一下3D模型, 理论上用上述的shader改一改就可以。 但是我还是建议3D模型用RenderTexture。比较好控制深度。