最近策划希望给游戏中的视频增加一个类似抖音的滤镜特效,如下所示:
可以看到,有条纹状和点状的干扰信息时不时出现在视频中。
我在网络上找了一下,还真的没有找到有人实现过这个滤镜的shader,因此只能靠我自己分析滤镜内容来反向推shader了。
首先我们可以发现,在这个特效中,干扰的效果都是呈现为方块的形状:
于是我想到可以将图片分割为一块一块的,具体的做法就是在frag shader里处理每个像素时,给它的uv坐标乘以一个倍数,并取其整数部分,利用它们来进行计算,判断该像素是否产生干扰:
float intx = floor(texcoord.x * 40.0);
float inty = floor(texcoord.y * 35.0);
float t = fmod(time, 5.0);
float line_disturb = sin(frac(inty * t + t * 4.5));
line_disturb *= step(max(0.8405, saturate(sign(sin(time)))), line_disturb);
float dot_disturb = sin(intx * intx * inty * t * 0.01);
dot_disturb = sign(dot_disturb * step(max(0.999995, saturate(sign(sin(time)))) , dot_disturb) * 0.5);
float disturb = max(line_disturb, dot_disturb);
texcoord = float2(fmod(texcoord.x + disturb * 0.2, 1.0), fmod(texcoord.y + disturb * 0.2, 1.0));
其中line_disturb决定了线段干扰的程度(图中干扰整行的部分),由inty决定;dot_disturb决定了格子干扰的程度,由intx和inty共同决定,并加入时间因素t,引入一些随机性。
在上述代码的最后一行,如果disturb为0,那么我们认为这个像素不需要干扰,那么只需正常地根据texcoord的值采样图片即可。若disturb大于0,那么我们会对该像素采样的位置引入一定偏移,产生干扰的效果。
经过以上修改后,效果如下:
可以看到产生了干扰效果,但是干扰的部分只是简单采样了图片内容,颜色本身没有变化。
接下来,我参考这篇文章给扰动的部分加了一个如下图的抖音特效:
代码如下所示:
diffuse = DiffuseTexture.Sample(DiffuseTexture_Sampler, texcoord);
float3 shift_color1 = DiffuseTexture.Sample(DiffuseTexture_Sampler, float2(texcoord.x + 0.01, texcoord.y + 0.01));
float3 shift_color2 = DiffuseTexture.Sample(DiffuseTexture_Sampler, float2(texcoord.x - 0.01, texcoord.y - 0.01));
float3 blend_first_color = float3(diffuse.r, diffuse.g, shift_color1.b);
float3 blend_3d_color = float3(shift_color2.r, blend_first_color.g, blend_first_color.b);
float sign_disturb = sign(disturb);
diffuse.rgb = sign_disturb * blend_3d_color + (1.0 - sign_disturb) * diffuse.rgb;
相当于是加以适当偏移采样了三次图片,然后将三次采样的颜色混合起来。若disturb为0则用正常采样的颜色diffuse,反之则用混合后的颜色。
加上颜色变换后,效果如下:
可以看到扰动的部分颜色明显有变化了。
完整frag shader的(伪)代码如下,去掉了一些不相干的代码:
float4 UIShowDrawPS(in VertexOutput IN) : SV_Target
{
float4 diffuse;
float2 texcoord = IN.TexCoord.xy;
float disturb = 0.0;
float intx = floor(texcoord.x * 40.0);
float inty = floor(texcoord.y * 35.0);
float t = fmod(time, 5.0);
float line_disturb = sin(frac(inty * t + t * 4.5));
line_disturb *= step(max(0.8405, saturate(sign(sin(time)))), line_disturb);
float dot_disturb = sin(intx * intx * inty * t * 0.01);
dot_disturb = sign(dot_disturb * step(max(0.999995, saturate(sign(sin(time)))) , dot_disturb) * 0.5);
disturb = max(line_disturb, dot_disturb);
texcoord = float2(fmod(texcoord.x + disturb * 0.2, 1.0), fmod(texcoord.y + disturb * 0.2, 1.0));
diffuse = DiffuseTexture.Sample(DiffuseTexture_Sampler, texcoord);
float3 shift_color1 = DiffuseTexture.Sample(DiffuseTexture_Sampler, float2(texcoord.x + 0.01, texcoord.y + 0.01));
float3 shift_color2 = DiffuseTexture.Sample(DiffuseTexture_Sampler, float2(texcoord.x - 0.01, texcoord.y - 0.01));
float3 blend_first_color = float3(diffuse.r, diffuse.g, shift_color1.b);
float3 blend_3d_color = float3(shift_color2.r, blend_first_color.g, blend_first_color.b);
float sign_disturb = sign(disturb);
diffuse.rgb = sign_disturb * blend_3d_color + (1.0 - sign_disturb) * diffuse.rgb;
diffuse *= IN.Color.a;
return diffuse;
}