@Unity
#基于Shader实现UGUI界面的溶解效果
前段时间要做一个类似的界面的切换效果,发现用溶解实现比较不错;
网上关于溶解效果的实现原理比较多,这里也不做过多的赘述,核心的思路就是对像素进行剔除,实在找不到的可以去这里看看。因为我们要做的效果是一个固定方向开始的效果,所以要在溶解的同时设定起始的一个坐标!
定义一个fixed2 的坐标 _NoiseCentrePos;需要注意的一点是,我们实际是和uv的坐标做运算,所以_NoiseCentrePos的取值范围也是[0 - 1]。
fixed2 _NoiseCentrePos;
要记得我们在声明这个变量的时候,这是一个verctor类型的数据。
_NoiseCentrePos("NoiseCentrePos", Vector) = (0.5, 0.5, 0) // 溶解的起始坐标
在这里我们暂且设定为从中心点开始溶解;下边开始介绍代码:
首先是Properties部分
Properties
{
_MainTex ("Texture", 2D) = "white" {} // 基础纹理
_NoiseTex("NoiseTex", 2D) = "white" {} // 溶解纹理
_Threshold("Threshold", float) = 1.3 // 阈值
_NoiseCentrePos("NoiseCentrePos", Vector) = (0.5, 0.5, 0) // 溶解的起始坐标
_FirstBroderColor("FirstBroderColor", Color) = (1, 1, 1, 1) // 第一组边缘颜色
_SecondBroderColor("SecondBroderColor", Color) = (1, 1, 1, 1) // 第二组边缘颜色
_ColorFactor("ColorFactor", Range(0, 1)) = 0.7
_DissoveEdge("DissoveEdge", Range(0, 1)) = 0.8
}
其次是我们的Tags,设置渲染队列
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
当然很重要的一点,我们知道溶解的原理是剔除像素,所以一定要混合透明度,使用最常用的就可以;当然也有其他的shaderlab的一些混合因子,也很感谢大佬的总结!
Blend SrcAlpha OneMinusSrcAlpha
至于pass部分就是我们需要用到的数据的声明和函数的定义了,例如:
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
....
sampler2D _MainTex;
float4 _MainTex_ST;
....
在顶点着色器中,我们不做过多的处理,就是简单的坐标转换,纹理的采样;这里我 使用的输入数据是“UnityCG.cginc” 中官方为我们定义好的appdata_base,当然我们也可以自己定义(我就是偷懒)
v2f vert (appdata_base v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); // 实例化处理
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _NoiseTex);
return o;
}
接下来要处理的是片段着色器部分;这部分主要处理溶解的方向和溶解边缘的一些颜色;
fixed4 frag (v2f i) : COLOR
{
fixed4 col = tex2D(_MainTex, i.uv.xy);
fixed4 nTex = tex2D(_NoiseTex, i.uv.zw);
// 设置溶解的方向
half dissove = nTex.r;
half dist = distance(_NoiseCentrePos, i.uv.zw);
dissove = dissove + dist;
col.a *= step(_Threshold, dissove);
clip(col.a - 0.01);
// 处理颜色部分
fixed t = _Threshold / dissove;
half lerpEdge = sign(t - _ColorFactor - _DissoveEdge);
fixed3 edgeColor = lerp(_FirstBroderColor.rgb, _SecondBroderColor.rgb, saturate(lerpEdge));
half lerpOut = sign(t - _ColorFactor);
fixed3 colorOut = lerp(col.rgb, edgeColor.rgb, saturate(lerpOut));
return fixed4(colorOut, col.a);
return col;
}
需要注意的是,在片段着色器中,我们尽量不要使用逻辑判断和for循环;至于原因这位大佬讲的就很详细;
到这里,如果你的界面上没有文字的话,这个shader就已经足够使用了,只需要你不断的变换shader的阈值就好了。
但是我在实际的应用中还需要用到Text 组件,需要连同Text中的内容一起溶解掉,所以使用了比较取巧的办法,那就是把界面的内容存储在一张Texture中,然后去溶解这个Texture就可以了;幸运的是,在Unity 中是可以这么去做的!我们只需要使用Texture2D这个类去实现。
下面简单的写一下C#代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Test : MonoBehaviour
{
public RawImage mRawImage;
private Texture2D mTex;
public Button onClick;
void Start()
{
mTex = new Texture2D(Screen.width, Screen.height / 2, TextureFormat.RGB24, false);
onClick.onClick.AddListener(() => {
StartCoroutine(Coroutine());
});
}
void Update()
{
}
///
/// 定义协程
///
///
public IEnumerator Coroutine()
{
yield return new WaitForEndOfFrame();
var rect = new Rect(0, 0, Screen.width, Screen.height / 2);
mTex.ReadPixels(rect, 0, 0);
mTex.Apply();
mRawImage.texture = mTex;
mRawImage.SetNativeSize();
}
}
这里需要介绍的是WaitForEndOfFrame这个协程指令,这个类似于是在等待摄像机渲染完成,如果不使用这个指令的话,就会出现下边的情况;这位大佬讲解的就比较详细
大概意思就是要从缓冲区去读取像素,而不是从绘图框架里;简单说就是,你的图应该先在Camera渲染完,存进缓冲之后再ReadPixels。
好了,接下来我们运行我们在程序,然后点击按钮看看会出现什么情况。
点击按钮之前我们在视图是这样的:
点击之后可以看到,我们已经把截图的一部分赋值给了rawimage中;
接下来就是把我们的RawImage中的Material 修改成我们的溶解材质,然后在代码中修改他的阈值就好了;我们可以使用计时器,或者在update中去处理;因为比较简单,这里就不写这部分代码了,直接看一下最后在实际项目中的效果:
因为在实际项目中是要从左下角开始溶解,所以我们就把_NoiseCentrePos的值设置为(0,0)就好了。
以上就是实现UGUI界面溶解效果的所有内容了,如果有任何的错误或者其他问题也欢迎各位大佬提出指正,总结不易,转载请注明出处:
https://blog.csdn.net/weixin_41767333/article/details/116791329