在体验各种美颜相机时,我发现FaceU和Snow相机都带一个小脸模式(或者边框模式),像这样的效果:
这是Snow相机的:
这是FaceU的:
两个看上去并不一样,因为Snow相机的默认模式就是场景优化模式(感觉Snow相机的边框更漂亮一些),不过这哪里小脸啦,图片还是一样大的好吗???!!!!
本文的目标,就是仿制一个这样的边框效果。
解决方案
相信大家也能看出来,其实就是对场景做了一个模糊的效果(模糊的半径略大呀),然后再把画面缩小贴出来,个人感觉Snow相机的处理更漂亮一些,不过也可能跟前期处理有关,FaceU的模糊显得很一般,仔细看都能看到条纹。
模糊(Blur)算法选择
毫无疑问,Blur这种耗时的操作,我们肯定希望使用GPU来加速完成,常见的模糊算法(低通滤波)有FastBlur/BoxBlur,就是我们常见的图像卷积模糊以及在其上的各种优化姿势(例如积分图、线性可分等),以及高斯模糊,利用高斯分布(正太分布,正泰分布,正态分布。。终于打对了,好艰辛)来选择周围像素的权重。
对了,高斯模糊也是可以降维的,也就是两个维度的操作互不相关。
如果感兴趣,可以自己去找找模糊算法的相关资料。
较为复杂的FastBlur的代码如下(这里的卷积核不再是一个矩形,而是一个随机的球形卷积核)
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D sTexture;
uniform vec3 iResolution;
vec2 Circle(float Start, float Points, float Point)
{
float Rad = (3.141592 * 2.0 * (1.0 / Points)) * (Point + Start);
return vec2(sin(Rad), cos(Rad));
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy;
vec2 PixelOffset = 1.0 / iResolution.xy;
float Start = 2.0 / 14.0;
vec2 Scale = 0.66 * 4.0 * 2.0 * PixelOffset.xy;
vec3 N0 = texture2D(sTexture, uv + Circle(Start, 14.0, 0.0) * Scale).rgb;
vec3 N1 = texture2D(sTexture, uv + Circle(Start, 14.0, 1.0) * Scale).rgb;
vec3 N2 = texture2D(sTexture, uv + Circle(Start, 14.0, 2.0) * Scale).rgb;
vec3 N3 = texture2D(sTexture, uv + Circle(Start, 14.0, 3.0) * Scale).rgb;
vec3 N4 = texture2D(sTexture, uv + Circle(Start, 14.0, 4.0) * Scale).rgb;
vec3 N5 = texture2D(sTexture, uv + Circle(Start, 14.0, 5.0) * Scale).rgb;
vec3 N6 = texture2D(sTexture, uv + Circle(Start, 14.0, 6.0) * Scale).rgb;
vec3 N7 = texture2D(sTexture, uv + Circle(Start, 14.0, 7.0) * Scale).rgb;
vec3 N8 = texture2D(sTexture, uv + Circle(Start, 14.0, 8.0) * Scale).rgb;
vec3 N9 = texture2D(sTexture, uv + Circle(Start, 14.0, 9.0) * Scale).rgb;
vec3 N10 = texture2D(sTexture, uv + Circle(Start, 14.0, 10.0) * Scale).rgb;
vec3 N11 = texture2D(sTexture, uv + Circle(Start, 14.0, 11.0) * Scale).rgb;
vec3 N12 = texture2D(sTexture, uv + Circle(Start, 14.0, 12.0) * Scale).rgb;
vec3 N13 = texture2D(sTexture, uv + Circle(Start, 14.0, 13.0) * Scale).rgb;
vec3 N14 = texture2D(sTexture, uv).rgb;
float W = 1.0 / 15.0;
vec3 color = vec3(0,0,0);
color.rgb =
(N0 * W) +
(N1 * W) +
(N2 * W) +
(N3 * W) +
(N4 * W) +
(N5 * W) +
(N6 * W) +
(N7 * W) +
(N8 * W) +
(N9 * W) +
(N10 * W) +
(N11 * W) +
(N12 * W) +
(N13 * W) +
(N14 * W);
}
void main() {
mainImage(gl_FragColor, vTextureCoord);
}
其实利用随机来做模糊是一个很棒的选择,但是有些低端移动GPU对于随机算法的支持非常烂,所以这就成为了一个备选方案。
But,高斯模糊和BoxBlur在半径较大的时候就歇菜了,因为是需要预处理(求和)的,如果我们使用OpenGL来做模糊半径一般都是3,那么在半径很大时就会出现很奇怪的重影效果。像GPUImage的高斯模糊在半径为20时的效果就成了这样(当然这也是和纹理大小以及GPU相关的):
模糊做成这样我也是醉了
怎么办呢,有两种解决方案:
- 在GPUImage的IOS项目中,高斯模