这个系列文章我们来介绍一位海外工程师如何探索 GPUImage 音视频技术,对于想要开始学习音视频技术的朋友,这些文章是份不错的入门资料,本篇介绍 GPUImage 创建自定义过滤器。
——来自公众号“关键帧Keyframe”的分享
GPUImage 最强大的特性之一,就是能够使用 OpenGL ES 2.0 着色器程序创建你自己的图像处理滤镜。本指南将带你从零开始,逐步完成从简单颜色调整到复杂多输入特效的全部流程。
1、核心架构
GPUImage 采用管线(pipeline)架构:
- 源对象(如
GPUImageVideoCamera、GPUImagePicture)负责捕获或加载图像 - 滤镜对象(
GPUImageFilter的子类)通过 OpenGL 着色器处理图像 - 输出对象(如
GPUImageView、GPUImageMovieWriter)将结果展示或保存
所有滤镜都遵循 GPUImageInput 协议,可接收上游的纹理输入,同时也能拥有多个下游目标,形成分支处理路径。
2、创建第一个自定义滤镜
2.1、基本结构
写一个片段着色器(GLSL)并初始化 GPUImageFilter。
// 从文件加载着色器
GPUImageFilter *customFilter = [[GPUImageFilter alloc]
initWithFragmentShaderFromFile:@"MyCustomShader"];
// 或者直接使用字符串
NSString *shaderString = @"...你的 GLSL 代码...";
GPUImageFilter *customFilter = [[GPUImageFilter alloc]
initWithFragmentShaderFromString:shaderString];
2.2、片段着色器模板
片段着色器必须包含以下固定内容:
// MyCustomShader.fsh
varying highp vec2 textureCoordinate; // 当前像素的纹理坐标(0.0–1.0)
uniform sampler2D inputImageTexture; // 输入图像纹理
void main()
{
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
// ↓↓↓ 在这里编写你的处理逻辑 ↓↓↓
lowp vec4 outputColor = textureColor;
gl_FragColor = outputColor;
}
2.3、示例:颜色反转
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{
lowp vec4 c = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4(1.0 - c.rgb, c.a); // 仅反转 RGB,保持 Alpha
}
3、带可调参数的滤镜
多数滤镜需要可调参数(如强度、程度)。做法:
- 新建
GPUImageFilter子类 - 定义公开属性
- 在 shader 中用
uniform接收参数
3.1、示例:可调对比度滤镜
3.1.1、着色器 ContrastFilter.fsh
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float contrast; // ← 可调参数
void main()
{
lowp vec4 c = texture2D(inputImageTexture, textureCoordinate);
c.rgb = ((c.rgb - 0.5) * contrast) + 0.5; // 对比度公式
gl_FragColor = c;
}
3.1.2、Objective-C 封装
// GPUImageContrastFilter.h
@interface GPUImageContrastFilter : GPUImageFilter
@property (nonatomic) CGFloat contrast;
@end
// GPUImageContrastFilter.m
@implementation GPUImageContrastFilter {
GLint contrastUniform;
}
@synthesize contrast = _contrast;
- (instancetype)init {
if (self = [super initWithFragmentShaderFromFile:@"ContrastFilter"]) {
contrastUniform = [filterProgram uniformIndex:@"contrast"];
self.contrast = 1.0;
}
returnself;
}
- (void)setContrast:(CGFloat)value {
_contrast = value;
[self setFloat:value forUniform:contrastUniform program:filterProgram];
}
@end
使用:
GPUImageContrastFilter *f = [[GPUImageContrastFilter alloc] init];
f.contrast = 1.5; // 提升 50% 对比度
4、进阶技巧
4.1、多输入滤镜
GPUImage 已内置:
GPUImageTwoInputFilterGPUImageThreeInputFilterGPUImageFourInputFilter
示例:两张图的混合
GPUImagePicture *bg = [[GPUImagePicture alloc] initWithImage:bgImg];
GPUImagePicture *fg = [[GPUImagePicture alloc] initWithImage:fgImg];
GPUImageAlphaBlendFilter *blend = [[GPUImageAlphaBlendFilter alloc] init];
blend.mix = 0.5;
[bg addTarget:blend];
[fg addTarget:blend]; // 顺序决定前后景
[bg processImage];
[fg processImage];
4.2、自定义双输入滤镜
着色器示例(两图混合):
varying highp vec2 textureCoordinate;
varying highp vec2 textureCoordinate2; // 第二张图的坐标
uniform sampler2D inputImageTexture; // 背景
uniform sampler2D inputImageTexture2; // 前景
uniform lowp float mixValue;
void main() {
lowp vec4 c1 = texture2D(inputImageTexture, textureCoordinate);
lowp vec4 c2 = texture2D(inputImageTexture2, textureCoordinate2);
gl_FragColor = mix(c1, c2, mixValue);
}
4.3、卷积与多点采样
边缘检测简化版:
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp float texelWidth, texelHeight;
void main() {
highp vec2 dx = vec2(texelWidth, 0.0);
highp vec2 dy = vec2(0.0, texelHeight);
lowp float s[9];
// 3×3 邻域采样
s[0] = texture2D(inputImageTexture, textureCoordinate - dx - dy).r;
...
// Sobel 计算
highp float gx = -s[0] + s[2] - 2.0*s[3] + 2.0*s[5] - s[6] + s[8];
highp float gy = -s[0] - 2.0*s[1] - s[2] + s[6] + 2.0*s[7] + s[8];
gl_FragColor = vec4(vec3(length(vec2(gx, gy))), 1.0);
}
5、滤镜链与分支
5.1、线性链
[videoCamera addTarget:brightnessFilter];
[brightnessFilter addTarget:contrastFilter];
[contrastFilter addTarget:sepiaFilter];
[sepiaFilter addTarget:gpuImageView];
[videoCamera startCameraCapture];
5.1.1、分支路径
任意滤镜都可同时输出到多个目标,实现分屏、并行处理等效果。
6、性能优化
- 减少纹理采样:
texture2D()昂贵,能省则省 - 避免条件分支:
if会显著拖慢 GPU - 精度够用即可:颜色用
lowp,坐标用mediump,仅必要时用highp - 合并运算:复杂公式尽量化简
- 链长度:每多一级滤镜就多一次渲染开销
6.1、内存管理
如需捕获某一帧:
[filter useNextFrameForImageCapture];
[source processImage];
UIImage *out = [filter imageFromCurrentFramebuffer];
7、实战示例
7.1、自定义暗角(Vignette)
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp float vignetteStart, vignetteEnd;
void main() {
lowp vec4 c = texture2D(inputImageTexture, textureCoordinate);
highp float d = distance(vec2(0.5), textureCoordinate);
highp float v = smoothstep(vignetteEnd, vignetteStart, d);
gl_FragColor = vec4(c.rgb * v, c.a);
}
7.2、自定义 RGB 色相/饱和度调整
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp float redAdj, greenAdj, blueAdj;
void main() {
lowp vec4 c = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4(c.r*redAdj, c.g*greenAdj, c.b*blueAdj, c.a);
}
8、结语
掌握 GLSL 与 GPUImage 架构后,你就能为 iOS 应用打造独一无二的实时视觉效果。牢记:
- 从模板开始,逐步叠加功能
- 用
uniform暴露参数 - 继承
GPUImageFilter或其变体 - 用链式结构组合复杂特效
- 时刻关注实时性能
学习和提升音视频开发技术,推荐你加入我们的知识星球:【关键帧的音视频开发圈】

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。