最近在做故障艺术中的RGB Split效果。RGB分离的效果可以做很多变化,本篇文章的重点部分是在SelectiveRGB效果上,也就是单独的对屏幕中的某个东西做RGB效果。
因为之前也没写过屏幕后处理的东西,想记录这次过程中遇到的一些问题。
本次的屏幕后处理效果有参考毛前辈的效果,然后加上自己思考做了很多改动变化等。
首先,做屏幕后处理我们需要建立一个基本的屏幕后处理脚本系统。这个基类脚本用来检查一系列条件是否满足,比如是否支持渲染纹理和屏幕特效,是否支持当前的Unity Shader等等,并且在这个脚本中我们会有一个CheckShaderAndCreateMaterial的函数来帮我们检查并创建一个用于渲染纹理的材质。(这边可以参考Unity shader入门精要) 不详细赘述了。
RGB Split效果的实现原理是对屏幕图像的RGB三个通道用不同的UV偏离值进行采样。然后配合使用三角函数来达到抖动的目的。一般会使G通道不变,抖动R和B通道。根据选择抖动的方向在采样坐标上发生变化。
我将多种情况结合在一个shader中,只用一个脚本可以实现多种变化。
方向有:Horizontal, Vertical, Horizontal_Vertical
变化效果有三种我称之为:Normal,Periodic,Random。并且当选择Random的情况时,就不分三种方向了(试验过后感觉Random情况下没有必要分三种方向)
可调整的参数:
Speed,调整快慢,视觉上看就是变化一次的时间的快慢。
Amount,调整RGB分离的多少。
Fading,最后用来在分离图像和屏幕图像混合中做插值。
CenterFading,用来在像素距离屏幕中心点的差值distance和RGB分离值SplitAmount中做插值。CenterFading为1的时候,距离屏幕中心越远的像素点的分离值会越大。
AmountR 和 AmountB,可以分开控制 R通道和B通道分离的值的多少。
我想要的是,当选择Normal普通状态下,是没有Frequency这个参数的,当选择Periodic的时候,Frequency的属性就显示,当选择Random的时候,仅有Speed和Indensity参数可调控。(其实本来想把Frequency和indensity参数搞成一个的,但是实际上发现两个参数的数值调控没法特别统一)
Shader中,我总共包含了4个pass,分别是三个方向Horizontal,Vertical,和Horizontal_Vertical,以及当选择Random的时候进行随机处理的Pass。
并且通过上述参数的描述,我一开始就意识到如果每句代码都写出来一定会是一个非常冗长的形式,因此我就想能否有什么方式能够达到,根据不同的情况,在同一个shader中多次编译达到不同的效果呢?
于是我使用了shader feature这样的宏定义。通过使用了shader变种,即#pragma shader feature A 这样的形式。来达到根据不同情况,使用不同的预处理器指令,来多次编译Shader代码。
参考网上的文章可以知道,shader_feature与multi_compile 非常相似。唯一的区别是Unity shader_feature在最终版本中不包含未使用的着色器变体,而multi_compile会把所有的变种全部打包进去。(不详述网上文章很多)
我写的这个shader按上述情况来说应该包含八个变种。我们可以通过shader面板查看。
因此,我就可以达到,在不同情况下,通过属性面板的选择,其实就是在cs脚本通过开启EnableKeywords和关闭DiasbleKeywords开关来达到变种的目的。这样是为了不让代码变得特别冗长,而且这样会比在shader中写if条件句更好,在Shader写if语句会影响在GPU的执行效率,而开关其实是跟if条件语句作用一样的。
这边以Horizontal为例子。代码都经过很多修改试验,一次次的检验最终效果。
Shader中,这是HorizontalFrag的部分代码。我在最前面已经定义过宏
//在最前面定义了变种 名字为USING_NORMAL
#pragma shader feature USING_NROMAL
.........
//HorizontalFragment中的部分重点代码
#if USING_NORMAL
//use _Amount instead
strength = _Amount;
time = _TimeX * _Speed * 5;
splitAmount = (1.0 + sin (time)) * 0.5;
splitAmount = pow (splitAmount,6.0);
splitAmount *= 0.05 * strength;
splitAmountR = splitAmount;
splitAmountB = splitAmount;
#else
strength = 0.5 * sin(_TimeX * _Frequency) + 0.5;
Amount = _Amount * strength;
Amount *= 0.01;
time = _TimeX * _Speed ;
splitAmountR = sin (time * 0.2) * Amount;
splitAmountB = sin (time * 0.5) * Amount;
#endif
//center fading according to distance
float distance = length (i.uv - float2 (0.5,0.5));
splitAmountR *= lerp (1,distance,_CenterFading);
splitAmountB *= lerp (1,distance,_CenterFading);
half3 colorR = tex2D (_MainTex,float2 (i.uv.x + splitAmountR * _AmountR,i.uv.y))