How Unity's ScrollRect cuts particle effects, and how to make particle effects appear on the UI

In function development, sometimes some special effects are added to the UI for better effects, such as adding a ring particle effect on the avatar frame, but due to the different rendering methods of particles and UI, UI and special effects will appear Interspersed between, the display is not ideal. And if the character list is displayed under the ScrollRect, the particle effects cannot be masked when sliding the scroll bar. Here we focus on solving these two problems:

First solve how to make the particle effects displayed on the UI:

1. Set the Canvas rendering mode:

When adding a UI object, if there is no Canvas under the Hierarchy, the UGUI system will automatically add the object, and the default rendering mode of Canvas is Screen Space - Overlay. Canvas has three rendering modes:

Screen Space - Overlay: Regardless of any other 3D objects, the UI is always displayed at the front of the screen

Screen Space - Camera: At this time, there is a certain distance between the Camera and the Canvas, and some 3D objects can be added within this distance, such as particle effects, etc., and can be displayed on the UI by adjusting the Z coordinates of the particle objects. Of course, there are other ways to achieve this effect, which will be introduced later.

World Space: In this mode, the UI is the same as ordinary 3D space objects, such as the Sphere and Box that come with the engine.

Since particle effects belong to 3D objects, in order to enable particle effects to be displayed on the UI, it is necessary to change the rendering mode of Canvas to "Screen Space - Camera":

In this mode, you need to specify the rendering camera for the Canvas, and set the Layer and order in layer of this Canvas at the same time. After the setting is complete, all UIs under this Canvas belong to the same layer, and the Order in layer remains consistent. When rendering, the occlusion relationship between UIs is determined according to the order from top to bottom

2. Set the Order in Layer between UI and particle effects:

First, explain the rendering relationship between UI in detail :

Each UI is separated according to the Layer set by itself, and sorted according to Order in layer in the same layer. The larger the value, the more it is displayed on the top . UIs in the same order in layer are rendered sequentially from top to bottom in the Hirearchy. The latter will obscure the previously rendered UI .

The UI settings and display effects in the Hierarchy are as follows:

         

Under the same canvas, according to the rendering order from top to bottom, Red will definitely block the picture of Blue. So how can blue be displayed on red even if it is rendered before red? In this case, you need to change the layer of the UI or the Order in layer - generally changing the Order in layer can achieve the effect, so there is no need to change the layer layer

There are two ways to achieve the effect:

1. Because Red must block Blue under the same canvas, Red is extracted from Blue here. For demonstration purposes, Red is still placed under Blue. Then add Canvas to Blue to change Blue's Order in layer

    

Red is rendered under the default Canvas, so its layer - default, Order in layer - 0. When adding Canvas to Blue and enabling Override sorting, and setting Order in layer to 0, the UI that has been rendered by the Order in layer will be reset, and the UI after reset will be displayed by default — but only for the same screen coordinates Reset the area where two UIs overlap, and display the previous UI in the area where there is no overlap :

In the above case, Blue - size: 200 x 200, Red - size: 100 x 100 , when Blue's canvas is reset, Blue will reset the same Order in layer, so it will completely block Red. But if change Blue - size: 50 x 5 0, it will show:

It can be seen from the above that the so-called reset is only for the case where there are multiple UI displays in the same area, and those outside the area will not be affected

2. The principle of the second method is similar to the first one, but without changing the parent-child relationship between Blue and Red, directly change the Order in layer of Red. By default, both Blue and Red are in Layer - default, Order in layer - 0, and then rendered in order from top to bottom. To make Blue appear on top of Red, you need to reset Red.

  

Although a Canvas is added for Red to reset the Order in layer, since the Blue and Red rendering layers and order settings are the same, and the Red - size is small, even the reset will only affect a small area. So it is necessary to change the Order of Red so that Red is rendered after Blue, so that Blue can be displayed on Red. Since the default order in layer of Blue is 0, it is enough to set the order in layer of Red to "-1" here .

    

Note: UGUI merges Drawcalls according to Canvas. Different Canvas cannot merge Drawcalls even if they have the same layer and order in layer. Therefore, the overall UI layout of the project needs to be designed in advance . In order not to affect the later optimization, try not to add Canvas at will to change the UI rendering relationship

According to the above explanation, in order to display the particle effects on the UI, you only need to change the "Sorting layer" and "Order in layer" of the particle effects. Usually, changing the "Order in layer" can achieve the effect. The default "Order in layer" of the UI is "0", so just set the "Order in layer" of the particle effect to be greater than 0.

The running effect is as follows:

In this way, the particle effects are displayed on the UI. By setting the order in layer between the particles and the UI, the related UI interpenetration problem can be solved.

2. How to solve the problem that the particle effect cannot be masked when the ScrollRect slides?

First of all, we need to know how the mask mask works?

Here we need to introduce a Shader function of "Stencil Buffer - Stencil Buffer". When the Shader used by the material supports Stencil Buffer, you can customize the occlusion and culling relationship between UIs by adjusting the parameters.

1. Parameter analysis: the key parameters of the Stencil Buffer in Shader:

   Properties
    {
        .................

        _Stencil ("Stencil ID", Float) = 0
        _StencilComp ("Stencil Comparison", Float) = 8
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255
        _ColorMask("Color Mask", Float) = 15

        .................
    }

    SubShader
    {
        .................

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }
        
        ..................
        ..................
        ColorMask[_ColorMask]

        .................
        .................
     }

Usually the parameters changed when customizing effects are: Ref [_Stencil], Comp [_StencilComp], Pass [_StencilOp]

Ref: The referenceValue used by the UI, which will be compared with the value stored in the Stencil Buffer when calculating the color. For a shader with a stencil buffer function, each pixel of the UI will be marked with a referenceValue and stored in the stencil buffer, that is, stencilBufferValue = referenceValue, for subsequent comparison with other referenceValues. The value range is an integer from 0 to 255

The referenceValue value of the Stencil Buffer defaults to 0 in the initial state

Comp: The operation of comparing the Ref value with the value stored in the Stencil Buffer, such as greater than, equal to, less than, etc.

The situation of various operations is as follows: the value representing each operation is incremented sequentially from 0 - 8

 Pass: represents the operation performed on the pixel after the above-mentioned referenceValue comparison, and represents that the value of each operation is incremented from 0 to 7

 PS: Under normal circumstances, the ReadMask and WriteMask will not be modified, just keep the default values.

ReadMask :
从字面意思的理解就是读遮罩,readMask将和referenceValue以及stencilBufferValue进行按位与(&)操作,readMask取值范围也是0-255的整数,默认值为255,二进制位11111111,即读取的时候不对referenceValue和stencilBufferValue产生效果,读取的还是原始值。
WriteMask:
是当写入模板缓冲时进行掩码操作(按位与&),writeMask取值范围是0-255的整数,默认值也是255,即当修改stencilBufferValue值时,写入的仍然是原始值。


和深度测试一样,在unity中,每个像素的模板测试也有它自己一套独立的依据,具体公式如下:
if(referenceValue&readMask comparisonFunction stencilBufferValue&readMask)

通过像素

else

抛弃像素

通过的像素操作则根据 stencilOperation命令值 来确定,并更新模板缓冲内的值:

在更新模板缓冲值的时候,也有writeMask进行掩码操作,用来对特定的位进行写入和屏蔽,默认值为255(11111111),即所有位数全部写入,不进行屏蔽操作。

Detailed explanation of each parameter: UnityShader instance 09: Stencil Buffer&Stencil Test_lupeng's Blog-CSDN Blog_shader stencil

Notice:

When "_StencilComp" passes the test, the pixel will be drawn and displayed , but "_StencilOp" refers to processing the tag value corresponding to the pixel in the "Stencil Buffer" after passing the template or depth test. This is the same as Whether this pixel will be displayed is irrelevant, it is only for the value stored in the stencil buffer , the default is keep, that is, the referenceValue used by this UI will not be written into the stencil buffer, and the stencil buffer still retains the previous value Value - This value will continue to be used for the next "stencilComp" comparison

2. Stencil Buffer instance operation:

When creating a new UI image, you can see that the default shader used by the image is "UI/Default", which already supports the function of Stencil Buffer:

But because it uses the default material "Default UI Material" in UGUI, the parameters cannot be customized.

In order to achieve a custom occlusion effect, you can create a new materialA and set its shader to "UI/Default":

In this way, you can freely set the parameters to achieve the desired effect

As shown in the above parameters, by default referenceValue = 0, Comp = Always, Op = Keep

 As shown below, create two images, both using the default material:
  

Since the parameters remain default, when drawing Blue first, it always passes the stencil test, and Blue is drawn as a whole; then draws Red, and at the overlap of the two images, Red is drawn through the stencil test, so Red will partially cover Blue At this time , the data stored in the stencil buffer remains 0 because stencilOp = keep

According to the above situation, how to draw Blue and Red sequentially from top to bottom, and still display Blue at the overlap?

At this point, you can directly adjust the parameters, just set Red to not pass the stencil test:

Analysis: Because it is necessary to achieve the overlapping of Blue and Red, the Red cannot pass the stencil test, but the non-overlapping part can pass the stencil test, so it is necessary to make the stencil of the Blue display area and the non-display area before drawing Red The values ​​stored in the buffer are different. By default, stencilBufferValue = 0 in the stencil buffer, so you need to change Blue's referenceValue = 1, stencilOp = 2 (Replace), stencilComp = 8 (Always). After passing the stencil test, write the referenceValue into the stencilBufferValue according to the stencilOp.

  

At this time, the stencilBufferValue of the Blue drawing area = 1, and the stencilBufferValue of the non-drawing area remains at 0 by default. That is, the requirement is met.

For Red, the overlapping part cannot pass the stencil test, and the overlapping part has stencilBufferValue = 1. So set Red's referenceValue = 0, stencilComp = Equal(3), stencilOp = 0. Since the Equal comparison is used in the overlap, the Red must not pass the stencil test and will not be drawn, but the referenceValue = stencilBufferValue in the non-overlap, so the Red can be drawn through the stencil test . For the stencilOp parameter, there is no need to change it in general, and the default value of Keep is still maintained here . The effect is as follows:

PS: The function of parameters "Use Alpha Clip" and "Color Mask":

1. Parameter "Use Alpha Clip": As shown in the figure above, although the effect can be achieved, there are still some pixels in the lower left corner blocked:

This is because when the imported Texture is set to the "Sprite" format, Unity will automatically cut off the extra pixels of the texture, but some corners will still have a little residue. When "Use Alpha Clip" is checked:

After this parameter is checked, the pixels whose alpha is less than 0.001 in the image will be discarded automatically. In this way, when drawing the UI, it is equivalent to no pixels at all here, and it will not affect the occlusion between the UIs.

Note: If the alpha of a certain area of ​​a certain texture of the artist is very low, but because there are still pixels here, it will still block other UIs, and if stencilOp is replaced, the area with a very low alpha will still be written into the stencilBufferValue . To solve this problem, "Use Alpha Clip" will discard pixels with low alpha in the image

 

As shown in the above figure, although the alpha is very small, it will still block the pictures behind. When discarding pixels in this area,

 

 In this way, a better display effect can be obtained.

2. Parameter "Color Mask": Mask the pixels that pass the stencil test in this UI. Note that the mask here is only for itself. For example, pixels in this area have been drawn before, and this UI has not passed the stencil test. At this time, even if "Color Mask = 0" is set, it will not affect the previous pixels in this area.

Whether the pixel will be displayed depends on whether the pixel passes the stencil test and the value set by the color mask. By default, "color mask = 15", so as long as the pixel passes the stencil test it will be displayed.

For example, when setting BlueMat's "color mask = 0" - you can use this method to achieve some hollowing out effects

   

 Although Blue has passed the stencil test, it will not be displayed due to the color mask. And by adjusting the value of "Color Mask", different display effects can be obtained:

Color Mask = 5:                                                  Color Mask = 10:         

                                 

3. Add the stencil buffer function to the particle shader:

As shown below, add particle effects under Blue, and you can clearly see that the particles exceed the boundary of the image - the shader used here is "Legacy Shaders/Particles/Additive". The reason for this is that the particle shader does not have the Stencil Buffer function

 

 In order to achieve the mask effect, it is necessary to add a Stencil Buffer to the shader of the particle. Create a new Shader script, copy the shader source code used by the particle to the newly created shader script, and add the above-mentioned Stencil Buffer key parameters Property and Stencil to it, and create a new material to use the shader, so that you can freely adjust the parameters to achieve the effect .

For example: use Unity's built-in Shader here: "Particle Add.shader"

Create a new Shader - CustomParticleShader.shader, copy the source code of "Particle Add.shader", and add the key parameters of StencilBuffer to the CustomParticleShader.shader script file:

Shader "Custom/CustomParticleShader" 
{
    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

        _Stencil("Stencil ID", Float) = 0
        _StencilComp("Stencil Comparison", Float) = 8
        _StencilOp("Stencil Operation", Float) = 0
        _StencilWriteMask("Stencil Write Mask", Float) = 255
        _StencilReadMask("Stencil Read Mask", Float) = 255
        _ColorMask("Color Mask", Float) = 15
    }

        Category{
            Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" }
            
            Stencil
            {
            Ref[_Stencil]
            Comp[_StencilComp]
            Pass[_StencilOp]
            ReadMask[_StencilReadMask]
            WriteMask[_StencilWriteMask]
            }

            Blend SrcAlpha One
            ColorMask RGB
            Cull Off Lighting Off ZWrite Off
            ColorMask[_ColorMask]

            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;

                    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
                        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);
                        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
                        return col;
                    }
                    ENDCG
                }
            }
        }
}

At this point, the new material ParticleMat of CustomParticleShader.shader will be used to assign the particle effect:

 

The effect to be achieved is that when the particles exceed the Blue boundary, they will be automatically cut off and will not be displayed, so the parameters of ParticleMat need to be adjusted. Since the stencilBufferValue in the Blue area = 1 and the stencilBufferValue = 0 outside the area, you can set "referenceValue = 1, stencilComp = Equal(3), stencilOp = Keep(0)" in the ParticleMat, the effect is as follows:

The masking effect of such particle effects is also realized.

Project source code address: Blocking and masking functions between UI and particle effects in Unity-Unity3D Document Class Resources-CSDN Download

PS:

1. Unity's built-in Shader source code:

One is to download from the official website : https://unity3d.com/get-unity/download/archive

After selecting the corresponding platform and version, click "Built in shaders"

The second is to download it on Github . There is an open source project address: https://github.com/TwoTailsGames/Unity-Built-in-Shaders

Attached here is the downloaded Unity2018.4.1f1 built-in Shaders source code file: Unity2018.4.1f1 built-in Shaders source code file-Unity3D Documentation Resources-CSDN Download

2. The shader of the particle stencil buffer used above will have some problems in the display effect in actual use, causing the particles to appear square. You can use the following shaders: —— Support stencil buffer:

Shader "UI/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

	_StencilComp ("Stencil Comparison", Float) = 8
    _Stencil ("Stencil ID", Float) = 0
    _StencilOp ("Stencil Operation", Float) = 0
    _StencilWriteMask ("Stencil Write Mask", Float) = 255
    _StencilReadMask ("Stencil Read Mask", Float) = 255

    _ColorMask ("Color Mask", Float) = 15

    [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0

    // Soft Mask support
    // Soft Mask determines that shader supports soft masking by presence of this property.
    [PerRendererData] _SoftMask("Mask", 2D) = "white" {}
}

Category {
	Tags {
	    "Queue"="Transparent"
	    "IgnoreProjector"="True"
	    "RenderType"="Transparent"
	    "PreviewType"="Plane"
	    "CanUseSpriteAtlas"="True"
    }
	Blend SrcAlpha One
	ColorMask RGB
	Cull Off Lighting Off ZWrite Off

	SubShader {

		Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

		Pass {

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 2.0
			#pragma multi_compile_particles
			#pragma multi_compile_fog

			#include "UnityCG.cginc"
			#include "UnityUI.cginc"

            // Soft Mask Support
            // You also can use full path (Assets/...)
            #include "Assets/Plugins/SoftMask/Shaders/SoftMask.cginc"

            #pragma multi_compile __ UNITY_UI_ALPHACLIP

            // Soft Mask Support
            #pragma multi_compile __ SOFTMASK_SIMPLE SOFTMASK_SLICED SOFTMASK_TILED

			sampler2D _MainTex;
			fixed4 _TintColor;

			struct appdata_t {
				float4 vertex : POSITION;
				fixed4 color : COLOR;
				float2 texcoord : TEXCOORD0;
			};

			struct v2f {
				float4 vertex : SV_POSITION;
				fixed4 color : COLOR;
				float2 texcoord : TEXCOORD0;
				UNITY_FOG_COORDS(1)

				//#ifdef SOFTPARTICLES_ON
				//float4 projPos : TEXCOORD2;
				//#endif

                // Soft Mask Support
                // The number in braces determines what TEXCOORDn Soft Mask may use
                // (it required only one TEXCOORD).
                SOFTMASK_COORDS(2)
			};

			float4 _MainTex_ST;

			v2f vert (appdata_t IN)
			{
				v2f v;
				v.vertex = UnityObjectToClipPos(IN.vertex);
				//#ifdef SOFTPARTICLES_ON
				//v.projPos = ComputeScreenPos (v.vertex);
				//COMPUTE_EYEDEPTH(v.projPos.z);
				//#endif
				v.color = IN.color;
				v.texcoord = TRANSFORM_TEX(IN.texcoord,_MainTex);
				UNITY_TRANSFER_FOG(v,v.vertex);
                SOFTMASK_CALCULATE_COORDS(v, IN.vertex) // Soft Mask Support
				return v;
			}

			sampler2D_float _CameraDepthTexture;
			float _InvFade;

			fixed4 frag (v2f IN) : SV_Target
			{
				//#ifdef SOFTPARTICLES_ON
				//float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(IN.projPos)));
				//float partZ = IN.projPos.z;
				//float fade = saturate (_InvFade * (sceneZ-partZ));
				//IN.color.a *= fade;
				//#endif

				fixed4 col = 2.0f * IN.color * _TintColor * tex2D(_MainTex, IN.texcoord);
				UNITY_APPLY_FOG_COLOR(IN.fogCoord, col, fixed4(0,0,0,0)); // fog towards black due to our blend mode
                col.a *= SOFTMASK_GET_MASK(IN); // Soft Mask Support
				return col;
			}
			ENDCG
		}
	}
}
}

Guess you like

Origin blog.csdn.net/m0_47975736/article/details/122317276