Unity ShaderSpecial EffectsBorderFlow 边框流动(UI)

Posted 神码编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity ShaderSpecial EffectsBorderFlow 边框流动(UI)相关的知识,希望对你有一定的参考价值。

更新日期:2021年8月23日。
Github源码:[点我获取源码]

BorderFlow 边框流动

UI边框流动的效果非常常见,实现思路也五花八门,在此我就不贴网上的那些示例了,直接看我们最终的效果图:

思路分析

流光区域

我们首先将上下左右四个方向在流动的区域(青色)定义为流光区域,其他的定义为非流光区域,本质上也即是流光区域叠加了一个流光颜色(比如这里的青色)非流光区域就按图像的正常颜色输出即可。

那么,我们要如何确定哪些是流光区域呢?

这个其实很简单,我们通过上方的效果图就可以看出来,流光区域实际上就是一个矩形区域而已,那么如何确定一个矩形区域?

在一个二维平面中,只要确定了一个中心点(矩形的中心点),然后再确定了宽度高度,即可得到一个固定的矩形区域(我们不考虑旋转)。

众所周知,图像渲染就是一个不停输入与输出的过程,在这里我们不需要立即确定流光区域的真正值,我们只需要制定一个规则,让流光区域根据输入的值和我们制定的规则进行实时定义即可,规则如何制定?我们继续往后看。

流光区域的中心点

通过反复研究效果图,我们发现流光区域的位置并不是固定的,他会不停的以顺时针方向流动,以上、右的流光作为一组,下、左的流光作为一组,规则如下:

(上、右)流光从上方的左侧开始向右流动,抵达右边界后,再次从右方的上侧开始向下移动,抵达下边界后,流动停止(由于这里使用了循环动画,所以流光再次从上方的左侧开始),另一组(下、左)流光雷同,方向依旧顺时针。


如果将整个流动的过程定义为x=1的话,那么左上角就是x=0右上角就是x=0.5右下角就是x=1,如下图所示,整个流光的活动轨迹


那么,再次将流光区域看做一个矩形,这个矩形的中心点(也即是流光区域的中心点)的x坐标便可以确定了,那便是我们上文的x值(活动轨迹)。

至于中心点的y值,我们不难发现,上下左右四个方向的流光都是紧贴边缘的,而图像的四个边缘坐标都是已知的(uv坐标系),则y值很明显可以直接求出来:

比如上方流光区域中心点y值 = 1 - 流光区域高度 * 0.5

由此得出流光区域的中心点为:

流光区域的宽度、高度

中心点的规则确定了,那么再来看宽度高度

通过再次研究效果图,我们可以很明显的发现,流光区域的宽度和高度没有规则可言,高度可以是任意值(当然不会大于图像的高度),宽度也是一样。

也就是说,宽度高度的值我们可以直接定义为输入变量即可。

定义流光区域

思路理清楚了,接下来我们直接写代码定义流光区域,先设定好输入值:

	Properties
	{
		//流光位置:上文中流光的活动轨迹(0-1),也即是流光区域中心点的x坐标
		//由于流光区域中心点的y坐标可以通过厚度求得,所以这里不设直接输入
		_FlowPos("流光位置", Range(0, 1)) = 0
		_FlowWidth("流光区域宽度", Range(0, 1)) = 0.3
		_FlowThickness("流光区域高度", Range(0, 1)) = 0.03
	}

然后根据我们前文中的规则,算出流光区域的真正矩形区域所在(这里以上边框为例):

//根据规则,流光位置(_FlowPos)在0-0.5时,上边框的流光从左侧流动到右侧
//所以这里求出流光区域中心点的x坐标在整个上边框所占的比例 ratio 
half ratio = smoothstep(0, 0.5, _FlowPos);

然后将流光区域中心点的x坐标比例 ratio 映射到图像上的真实位置:

//half realXPos = lerp(0, 1, ratio);
half realXPos = lerp(_FlowWidth * -0.5, 1 + _FlowWidth * 0.5, ratio);
half realYPos = 1 - _FlowThickness * 0.5;

这里有人可能会看不懂了,上边框从左至右的真实x值应该是0-1(注释那行),为什么这里却是另一个值呢?

很简单,看图你就明白了:


因为将上边框区间的左、右都拉伸半个流光区域宽度,会给人一种流光从无到有从整到零的过渡流动过程,不至于一开始就有半截的流光显示在图像上。

那么至此,我们已经求出了流光区域的中心点,和已知的宽度高度,已然可以确定一个矩形区域,那么只要uv坐标在该区域的像素,就会被叠加流光效果,反之则无视。

流光效果

叠加流光效果的过程就简单了,首先,我们还是希望流光效果的亮度和颜色都是可变的,所以我们再次新增输入参数:

	Properties
	{
		//流光位置:上文中流光的活动轨迹(0-1),也即是流光区域中心点的x坐标
		//由于流光区域中心点的y坐标可以通过厚度求得,所以这里不设直接输入
		_FlowPos("流光位置", Range(0, 1)) = 0
		_FlowWidth("流光区域宽度", Range(0, 1)) = 0.3
		_FlowThickness("流光区域厚度", Range(0, 1)) = 0.03

		_FlowColor("流光颜色", Color) = (1,1,1)
		_FlowBrightness("流光亮度", Range(0, 1)) = 1
	}

接下来,我们判断当前输入的像素点的uv坐标是否在流光区域矩形内,在的话就叠加流光颜色流光亮度,不在的话,就原封不动输出。

	//IsInRect:求一个二维点是否在一个矩形内
	//第一个参数为一个矩形(half4 格式,四个值分别为【矩形中心点x坐标,矩形中心点y坐标,矩形宽度,矩形高度】)
	//第二个参数为指定的二维点
	//返回值:当输入的二维点在矩形区域内时,返回1,否则返回0
	//在这里我们将亮度 _FlowBrightness * IsInRect的返回值,使得在流光区域内的返回亮度值,不在的返回0
	half brightness = IsInRect(half4(realXPos , realYPos , _FlowWidth, _FlowThickness), uv) * _FlowBrightness;
	//将流光颜色叠加到主颜色,如果不在流光区域内,则上方求得的亮度为0,则叠加无效
	color.rgb += color.a * brightness * _FlowColor;

那么,求出来一个上边框以后,其他三个边框的算法也雷同了,只是流光区域所在的矩形定义略有不同(比如计算右边框时,_FlowWidth 将成为高度,_FlowThickness 将成为宽度),这里就不再赘述了。

如何求一个二维点在一个矩形内

这里也即是IsInRect的具体实现,其实很简单,看下算法就明白了:

//求一个点是否在指定方形区域内
fixed IsInRect(half4 rect, half2 point2)
{
	half width = rect.z * 0.5;
	half height = rect.w * 0.5;
	//如果点的位置没有超过方形区域左边界
	fixed left = step(rect.x - width, point2.x);
	//如果点的位置没有超过方形区域右边界
	fixed right = step(point2.x, rect.x + width);
	//如果点的位置没有超过方形区域上边界
	fixed up = step(rect.y - height, point2.y);
	//如果点的位置没有超过方形区域下边界
	fixed down = step(point2.y, rect.y + height);
	//则点在方形区域内(任何一边不满足,都将返回0)
	return left * right * up * down;
}

流光效果的平滑

我们再次回看一下效果图,会发现流光区域是呈现一个从右至左变淡的平滑效果的(上边框),也就是给人一种流动中的速度感


这点要怎么实现?我们还是看代码,一行搞定:

	//IsInRect:求一个二维点是否在一个矩形内
	//第一个参数为一个矩形(half4 格式,四个值分别为【矩形中心点x坐标,矩形中心点y坐标,矩形宽度,矩形高度】)
	//第二个参数为指定的二维点
	//返回值:当输入的二维点在矩形区域内时,返回1,否则返回0
	//在这里我们将亮度 _FlowBrightness * IsInRect的返回值,使得在流光区域内的返回亮度值,不在的返回0
	half brightness = IsInRect(half4(realXPos , realYPos , _FlowWidth, _FlowThickness), uv) * _FlowBrightness;
	//【新增的行】将流光区域平滑(使得越靠近区域右侧,流光强度越接近1,越靠近区域左侧,流光强度越接近0)
	brightness *= smoothstep(0, _FlowWidth, uv.x - realXPos + _FlowWidth * 0.5);
	//将流光颜色叠加到主颜色,如果不在流光区域内,则上方求得的亮度为0,则叠加无效
	color.rgb += color.a * brightness * _FlowColor;

这里的uv.x - realXPos + _FlowWidth * 0.5计算,将uv的x坐标映射到了(0,1),使得位于流光区域最左侧的点映射为0流光区域最右侧的点映射为1,从而生成了一个平滑区间的效果,将此平滑区间 * brightness ,便将叠加的流光颜色进行了平滑效果。

流光动画

要实现流光动画,很明显我们只需要改变输入的_FlowPos值即可,不需要借助_Time参数,直接添加一个动画播放器:


到此,这个简单的边框流动效果就基本完结了,如果还有不明白的地方可以查阅源码,当然,博客中的代码不一定与源码完全相同,这里的本意只是介绍思路,具体的实现过程中可能有一些语句上的优化,当然核心算法是一样的。

以上是关于Unity ShaderSpecial EffectsBorderFlow 边框流动(UI)的主要内容,如果未能解决你的问题,请参考以下文章

Unity ShaderSpecial EffectsScan 扫描(UI)

Unity ShaderSpecial EffectsScan 扫描(UI)

Unity ShaderSpecial EffectsScan 扫描(UI)

Unity ShaderSpecial EffectsCorrect 色彩修正(UI)

Unity ShaderSpecial EffectsCorrect 色彩修正(UI)

Unity ShaderSpecial EffectsCorrect 色彩修正(UI)