后处理——深入相机变形特效
后处理(Post-processing),是针对原有的游戏画面进行算法加工,达到提升画面质量或增强画面效果的技术,可通过着色器Shader程序实现。
概述
变形特效是处理和增强画面效果的一类后处理技术,经常被应用在各类相机短视频app特效中,如美颜瘦身、哈哈镜特效。
如下图,采用简单的一次函数θ = -A/R *d + A,其中A表示扭曲中心的旋转角度,A为正数则表示旋转方向为顺时针,负数表示逆时针,R表示扭曲的边界;
如下图右侧,我们通过将等距的黑色采样圈映射到更内聚的红色采样圈,使新采样圈之间的间距由内到外单调递增。
上图的函数表明,在靠近膨胀中心处,采样圈缩放最明显,缩放值最小(1 - S);随着dist增大,缩放值scale往1递增,直至到达R边界范围后,scale恒定为1,采样圈不再缩放。
float scale = (1.- S) + S * smoothstep(0.,1., dist / R); // 计算膨胀采样半径缩放值
于是我们得到上述采样半径缩放公式,其中设定Strength(0 < S < 1)代表膨胀程度。 对于膨胀距离场的变换过程,很容易推断出,要实现膨胀的反向效果收缩,直接让S位于[-1,0]区间即可。
如下图,绿色作用点P作为挤压起点,箭头为挤压向量V,其中向量方向指明挤压的方向,向量长度length(V)代表挤压的距离,向量终点为挤压后的位置。 要实现纹理挤压,就是让采样圈圆心往挤压向量V上偏移,采样中心点应平移到点P的位置。
公式:offset = length(V) - length(V) * smoothstep(0, R, dist),其中R表示挤压边界range。
同样的,我们引入时间变量time动态改变挤压向量的长度和方向,可以实现抖动特效,如上图小丑顶胯效果,具体shader代码如下:
#iChannel0 "src/assets/texture/joker.png"
#define RANGE .25 // 变形范围
#define PINCH_VECTOR vec2( sin(iTime * 10.), cos(iTime * 20.)) * .03 // 挤压向量
vec2 pinch(vec2 uv, vec2 targetPoint, vec2 vector, float range)
{
vec2 center = targetPoint + vector;
float dist = distance(uv, targetPoint);
vec2 point = targetPoint + smoothstep(0., 1., dist / range) * vector;
return uv - center + point;
}
void mainImage(out vec4 fragColor, vec2 coord) {
vec2 uv = coord / iResolution.xy;
vec2 mouse = iMouse.xy / iResolution.xy;
uv = pinch(uv, mouse, PINCH_VECTOR, RANGE);
vec3 color = texture(iChannel0, uv).rgb;
fragColor.rgb = color;
}
总结
本文主要介绍三类局部变形shader的实现原理,其中膨胀/收缩和挤压效果是通过采样距离场变换实现的,前者变换的是采样圈大小,后者变换的是采用圈位置。 除了上文的介绍的三种局部变形,还有一些比较有趣的全局变形效果,比如波浪特效(wave effect),错位特效和镜像等,shader实现比较容易,就不多做介绍了。
预览代码与效果
扭曲:https://www.shadertoy.com/view/slfGzN 膨胀/缩放:https://www.shadertoy.com/view/7lXGzN 挤压/拉伸:https://www.shadertoy.com/view/7tX3zN
参考资料:
glsl基础变换:https://thebookofshaders.com/08/?lan=ch Photoshop挤压特效算法:https://blog.csdn.net/kezunhai/article/details/41873775