动态滤镜

滤镜

音视频编辑工具中,对图像或者视频加滤镜是一种常见的做法,普通滤镜很好理解,一张LUT图就搞定了,LUT图一般设计师会提供好,开发拿过来转成纹理,opengl做下颜色转换就好了,这里就不多说了。

但是今天想讲的是动态滤镜,也有叫特效的,动效等等。比如小红书/剪映 等等,感受一下就大概知道了。

动态滤镜

先上一个效果感受下:

动态滤镜

视频:


很明显动态滤镜跟普通滤镜根本上就不是一回事了,所以更像是动效 特效了,这里不纠结叫什么,重点是关注下怎么做的呢?直观感受就是静态图片上覆盖了一帧帧的透明图片,确实就是这么一回事。

用一帧帧的带alpha通道的图片集,通过opengl blend一帧帧融合也可以实现, 弊端很明显,虽然这种动态效果一般也只有几秒,按30fps,也需要百来张图,图片资源很大,gif格式也会存在同样的问题。如果用视频呢? 但是视频没有alpha通道。。

方案

参考腾讯动画方案 vap 我们用一种特殊的视频,视频的一半表示rgb 另一半表示alpha通道。

表示alpha通道的 rgb 三个分量值一样,所以是灰度图效果,知道这种特殊的视频构成 接下来就好处理了

1
2
3
4
5
6
7
8
9
10
11
12
(
precision mediump float;
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;

void main() {
vec4 textureColor = texture2D(inputImageTexture,textureCoordinate);
vec4 leftColor = texture2D(inputImageTexture,vec2(textureCoordinate.x / 2.0,textureCoordinate.y));
vec4 rightColor = texture2D(inputImageTexture,vec2(textureCoordinate.x / 2.0 + 0.5,textureCoordinate.y));
gl_FragColor = vec4(leftColor.rgb * rightColor.b ,rightColor.b);
}
);

实际应用中,借用GPUImageMoive来解码一半一半的视频,然后自定义filter,fragmentshader 就是上面的这段,最后渲染到GPUImageView上,就达到了在静态图片上播放透明视频的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@interface GPUImageCustomFilter : GPUImageFilter
@end

@implementation GPUImageCustomFilter

- (id)init {
if (self = [super initWithFragmentShaderFromString:kGPUImageCustomFragmentShaderString]) {
}
return self;
}

/// size width / 2
- (void)setInputSize:(CGSize)newSize
atIndex:(NSInteger)textureIndex {
[super setInputSize:CGSizeMake(newSize.width / 2.0, newSize.height)
atIndex:textureIndex];
}

@end

使用customFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
NSURL *url = [NSURL fileURLWithPath:videoPath];
CGRect frame = previewView.bounds;

GPUImageView *gpuImageView = [[GPUImageView alloc] init];
gpuImageView.backgroundColor = [UIColor clearColor];
gpuImageView.fillMode = kGPUImageFillModePreserveAspectRatioAndFill;
gpuImageView.frame = frame;

GPUImageMovie *movie = [[GPUImageMovie alloc] initWithURL:url];
movie.shouldRepeat = YES;
movie.playAtActualSpeed = YES;
movie.runBenchmark = YES;

GPUImageCustomFilter *customFilter = [[GPUImageCustomFilter alloc] init];

[movie addTarget:customFilter];
[customFilter addTarget:gpuImageView];
[movie startProcessing];