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下面, 就可以进行裁切了。。

Shader相关Mask裁切UI粒子特效或者3D模型

在说一下3D模型, 理论上用上述的shader改一改就可以。 但是我还是建议3D模型用RenderTexture。比较好控制深度。