IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity的ScrollRect如何裁切粒子特效,以及如何使粒子特效显示在UI上 -> 正文阅读

[游戏开发]Unity的ScrollRect如何裁切粒子特效,以及如何使粒子特效显示在UI上

在功能开发中,有时候为了更好的效果会在UI上添加一些特效,比如在头像框上增加一个圆环的粒子特效,但由于粒子和UI的渲染方式有些不同,导致会出现UI和特效之间穿插,显示上不理想。并且如果在ScrollRect下显示人物列表,滑动滚动条时还无法对粒子特效进行遮罩处理。这里集中解决这两个问题:

首先解决如何使粒子特效显示在UI上:

1.设定Canvas渲染模式:

当添加一个UI对象时,如果Hierarchy下没有Canvas,则UGUI系统会自动添加该对象,并且Canvas的渲染模式默认为Screen Space - Overlay。Canvas有三种渲染模式:

Screen Space - Overlay:无论其他任何3D物体,UI始终显示在屏幕最前方

Screen Space - Camera:此时Camera与Canvas有一定的距离,可以在这段距离内添加一些3D物体,如粒子特效等,通过调节粒子物体的Z坐标即可在UI上显示出来。当然为了实现这个效果也有别的方式,后面会介绍

World Space:这种模式下,UI就和普通的3D空间物体,比如引擎自带的Sphere、Box等一样。

由于粒子特效属于3D物体,为了使得粒子特效能够显示在UI上,这里需要改变Canvas的渲染模式为“Screen Space - Camera”:

这种模式下需要为Canvas指定渲染相机,同时设置本Canvas的Layer,以及order in layer。设置完成后,本Canvas下的所有UI都属于同一层,并且Order in layer保持一致,渲染时根据从上到下的顺序决定UI之间的遮挡关系

2.设置UI和粒子特效之间的Order in Layer:

首先具体解释下UI之间的渲染关系

各个UI根据自身设定的Layer分隔,在同一layer根据Order in layer进行排序,数值越大越显示在上面。在同一order in layer的UI则按照Hirearchy中从上到下的顺序依次渲染。后面的会遮挡之前渲染的UI

如下图Hierarchy中UI设置以及显示效果如下:

? ?? ? ??

在同一canvas下,按照从上到下的渲染顺序,Red一定会遮挡住Blue的画面。那么如何能够做到即使Blue在Red之前渲染,也依然能可以显示在Red上面呢?这种情况就需要改变UI的layer或者Order in layer —— 一般情况下改变Order in layer就可以达到效果,所以也用不着改变layer层了

为了实现效果有两种方式:

一、因为同一canvas下Red必定会挡住Blue,因此这里把Red从Blue下提取出来,这里为了演示的需要,仍然将Red放在Blue之下。然后给Blue添加Canvas用于改变Blue的Order in layer

? ??

Red在默认的Canvas下渲染,因此其layer - default, Order in layer - 0。当给Blue添加Canvas并开启Override sorting,设置Order in layer 为0后,会将该Order in layer已渲染完成的UI进行重置,默认显示重置之后的UI —— 但仅针对同一屏幕坐标有多个UI重叠的区域进行重置,在没有重叠的区域,则依然显示之前的UI

上述情况Blue - size: 200 x 200, Red - size: 100 x 100,当重置Blue的canvas后,Blue会将同一Order in layer重置,因此会完全挡住Red。但如果改变Blue - size:50 x 50,则会显示:

由上可知,所谓的重置也仅仅只是针对同一区域有多个UI显示的情况,区域外的则不会受到影响

二、第二种方式和第一种的原理类似,但不用改变Blue 和 Red之间的父子关系,直接改变Red的Order in layer。在默认情况下,Blue 和 Red都在 Layer - default, Order in layer - 0,然后按照从上到下的顺序渲染,要使得Blue显示在Red上面,则需要重置Red。

??

虽然为Red添加了Canvas来重置Order in layer,但由于Blue 和 Red渲染layer、order设置相同,且Red - size较小,即使重置也只会影响很小的区域。所以就必须改变Red的Order,使得Red在Blue之后渲染,这样就可以实现Blue显示在Red上面了。由于Blue默认order in layer为0,因此这里设置Red的order in layer为“ -1”即可

? ??

注意:UGUI在进行Drawcall合并时是按照Canvas来进行的,不同的Canvas即使layer,order in layer相同,也无法合并Drawcall。因此对于项目整体UI布局需要事先设计好,为了不影响后期优化,尽量不要随意添加Canvas来改变UI渲染关系

根据以上的解释,要使得粒子特效显示在UI上则只需要改变粒子特效的“Sorting layer” 和 “Order in layer”,通常情况下改变“Order in layer”即可达到效果。UI默认的“Order in layer”为“0”,因此只要设置粒子特效的“Order in layer”大于0即可。

运行效果如下:

如此粒子特效就显示在UI上了,通过设置粒子和UI之间的order in layer即可解决相关的UI穿插问题

2.如何解决ScrollRect滑动时粒子特效无法被遮罩处理的问题?

首先我们需要知道遮罩Mask是如何起作用的?

这里需要引入一个“模板缓存 - Stencil Buffer”的Shader功能,当材质所使用的Shader支持Stencil Buffer时,则可以通过调节参数来自定义UI之间的遮挡剔除关系。

1.参数解析:Shader中的Stencil Buffer的关键参数:

   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]

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

通常自定义效果时改变的参数为:Ref [_Stencil],?Comp [_StencilComp],?Pass [_StencilOp]

Ref:该UI所使用的referenceValue,在计算颜色时会把该数值与Stencil Buffer中存储的数值进行比较。对于使用具有模板缓存功能的shader,会把该UI的各个像素点使用referenceValue进行标记并将该referenceValue存入模板缓存即stencilBufferValue = referenceValue,用于后续与其他referenceValue的比较。取值范围是 0 - 255 的整数

Stencil Buffer在初始状态下referenceValue数值默认为0

Comp:将Ref数值与Stencil Buffer中存储的数值进行比较的操作,如大于、等于、小于等。

各种操作的情况如下:代表各个操作的数值从0 - 8 依次递增

?Pass:代表通过上述的referenceValue比较后对该像素执行的操作,代表各个操作的数值从0 - 8 依次递增

?PS: 一般情况下并不会对ReadMask、WriteMask进行修改,保持默认值即可。

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),即所有位数全部写入,不进行屏蔽操作。

各个参数详解:UnityShader实例09:Stencil Buffer&Stencil Test_lupeng的博客-CSDN博客_shader stencil

注意:

当“_StencilComp”通过测试时,该像素点即会被绘制显示出来,但“_StencilOp”指的是通过模板或深度测试后,对“Stencil Buffer”中该像素点对应的标记值进行处理,这个与本像素点是否会显示出来是无关的,仅仅只是针对模板缓存中存储的数值进行操作,默认是keep,即本UI所使用的referenceValue并不会被写入模板缓存中,Stencil Buffer依然保留之前的数值——该数值会继续用于下次的“stencilComp”比较

2.Stencil Buffer实例操作:

当新建某个UI image时,可以看到该image默认使用的shader为“UI/Default”,其已经支持了Stencil Buffer的功能:

但由于其使用的是UGUI中默认的材质“Default UI Material”,因此无法自定义其中的参数。

为了实现自定义遮挡的效果,可以新建一个materialA,并设置其shader为“UI/Default”即可:

如此即可以自由设定参数实现想要的效果

如上参数所示,默认情况下referenceValue = 0, Comp = Always, Op = Keep

?如下所示,创建两个image,都使用默认的材质:
??

由于参数保持默认,因此先绘制Blue时,始终通过stencil test,Blue整个被绘制出来;然后绘制Red,在两张image的重叠处,Red通过stencil test被绘制出来,所以Red会将Blue进行部分遮挡此时stencil buffer中存储的数据由于stencilOp = keep,因此仍然保持为0

根据以上情况,如何在按照从上到下依次绘制Blue、Red的情况下,在重叠处依然显示Blue呢?

此时可以直接调节参数,只需要将Red设置为不通过stencil test即可:

解析:因为要实现在Blue与Red重叠处,对于Red不能通过stencil test,但是非重叠处则可以通过stencil test,因此这就必然要求在绘制Red之前就已经使得Blue显示区域和非显示区域的stencil buffer中存储的数值不同。默认情况下,stencil buffer中 stencilBufferValue = 0,因此需要改变Blue的referenceValue = 1,stencilOp = 2(Replace),stencilComp = 8(Always)。在通过stencil test后,根据stencilOp将referenceValue写入stencilBufferValue中。

??

此时Blue绘制区域的stencilBufferValue = 1,非绘制区域stencilBufferValue保持为默认的0。即满足了要求。

对于Red来讲,重叠处无法通过stencil test,而重叠处的stencilBufferValue = 1。因此设置Red的referenceValue = 0, stencilComp = Equal(3),stencilOp = 0。由于重叠处使用Equal比较,因此对于Red必然无法通过stencil test,不会被绘制,而非重叠处referenceValue = stencilBufferValue,所以通过stencil test,Red可以被绘制针对于stencilOp参数,一般情况不需要改变,这里依然保持默认值Keep。效果如下:

PS: 参数“Use Alpha Clip” 和 “Color Mask” 的作用:

1.参数“Use Alpha Clip”: 如上图所示,虽然可以实现效果,但左下角依然有部分像素遮挡:

这是由于在将导入的Texture设置为“Sprite”格式时,Unity会自动裁切掉该texture多余的像素,但有些边角依然会有少许残留。当勾选“Use Alpha Clip”后:

该参数勾选后,会自动将image中alpha小于0.001的像素舍弃掉。如此在绘制UI时则相当于此处完全没有像素,也就不会对UI之间的遮挡有影响。

注意:如果美术人员的某张texture部分区域alpha很低,但由于此处依然有像素,因此还是会挡住其他的UI,并且如果stencilOp为replace,则此alpha很低的区域依然会写入stencilBufferValue中。为了解决这个问题,“Use Alpha Clip”会将image中alpha很低的像素舍弃掉

?

如上图标记位置,虽然alpha很小,但依然会遮挡后面的图片。当舍弃该区域的像素时,

?

?如此则能得到更好的显示效果。

2.参数“Color Mask”:对本UI中通过stencil test的像素做屏蔽处理,注意这里的屏蔽仅仅只是针对自身而言。比如该区域之前已经有像素被绘制,本UI又没有通过stencil test,此时即使设置“Color Mask = 0”也不会对该区域之前的像素有影响

像素是否会显示出来取决于该像素是否通过stencil test,和 color mask设置的数值。默认情况下,“color mask = 15”,因此只要该像素通过stencil test即会在显示出来。

例如当设置BlueMat的“color mask = 0”时—— 可以使用这种方式达到一些镂空挖洞的效果

? ?

?虽然Blue已经通过了stencil test,但由于color mask的作用,其并不会显示出来。并且通过调节“Color Mask”的数值可以得到不同的显示效果:

Color Mask = 5:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Color Mask = 10:? ? ? ? ?

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

?

3.在粒子shader中加入stencil buffer功能:

如下所示,在Blue下添加粒子效果,可以明显的看到粒子超出了image的边界——这里使用的shader是"Legacy Shaders/Particles/Additive"。出现这样的原因在于粒子shader并没有Stencil Buffer功能

?

?为了实现遮罩效果,需要在粒子的shader中加入Stencil Buffer。新建Shader脚本,将该粒子所使用的shader源码拷贝到新建的shader脚本中,并在其中添加上述的Stencil Buffer关键参数Property 和 Stencil,并新建material使用该shader,这样就可以自由调节参数达到效果了。

例如:这里使用Unity内置的Shader:“Particle Add.shader”

新建Shader ——?CustomParticleShader.shader,拷贝“Particle Add.shader”的源码,并添加StencilBuffer关键参数到CustomParticleShader.shader脚本文件中:

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
                }
            }
        }
}

此时将使用CustomParticleShader.shader的新材质ParticleMat赋值给粒子特效:

?

要实现的效果是粒子超出Blue边界时会自动被裁切掉,不会显示出来,所以需要调节ParticleMat的参数。由于Blue区域的stencilBufferValue = 1,区域外的stencilBufferValue = 0,因此可以设置ParticleMat中“referenceValue = 1,stencilComp = Equal(3),stencilOp = Keep(0)”,效果如下:

如此粒子特效的遮罩效果也实现了。

项目源码地址:Unity中UI和粒子特效之间的遮挡以及遮罩功能-Unity3D文档类资源-CSDN下载

?

PS: Unity的内置Shader源码:

一是在官网下载https://unity3d.com/get-unity/download/archive

选择对应的平台和版本后,点击“Built in shaders”即可

二是在Github上下载,有个开源项目地址:https://github.com/TwoTailsGames/Unity-Built-in-Shaders

这里附上下载好的Unity2018.4.1f1的内置Shaders源码文件:Unity2018.4.1f1内置Shaders的源码文件-Unity3D文档类资源-CSDN下载

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-02-22 20:56:56  更:2022-02-22 20:58:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 16:30:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码