GPUImage(二):视频滤镜GPUImageFilter基础篇

按照GPUImage(一):视频采集GPUImageVideoCamera提到的示例demo FilterShowcase的路径,初始化videoCamera后设置GPUImageFilter。

GPUImageFilter

GPUImageFilter继承自GPUImageOutput,遵循GPUImageInput协议,遵循这个协议的对象都可以从响应链的上游接收处理过的纹理并继续处理,下游的处理对象称为上一步的target,响应链的下游可以有多个target(或分支)。
所以,GPUImageFilter就是用来接收源图像,通过自定义的顶点、片元着色器来渲染新的图像,并在绘制完成后通知响应链的下一个对象,既可以流入数据,也可以流出数据,GPUImageView,GPUImageMovieWriter是最终输出target,来显示图片或者视频。

GPUImageFilter.png

GPUImageSaturationFilter

直接看GPUImageFilter代码有些抽象,从GPUImageSaturationFilter入手更容易理解。当从FilterShowcase demo列表中选中Saturation时,filter操作——

filter = [[GPUImageSaturationFilter alloc] init];//filter初始化
.
.
[videoCamera addTarget:filter];//GPUImageVideoCamera添加filter作为target
.
.
GPUImageView *filterView = (GPUImageView *)self.view;//设置当前view为预览view
.
.
[filter addTarget:filterView];//filter添加预览view为最终target
.
.
[videoCamera startCameraCapture];//相机开始捕获

如果需要更改强度
[(GPUImageSaturationFilter *)filter setSaturation:[(UISlider *)sender value]]; break;
简单几步,即完成了调用和强度设置。
GPUImageSaturationFilter的.h文件只有一个saturation属性用于修改,.m文件分为两个部分,第一部分是一个做饱和度调节的片段着色器,这个着色器出自《图形着色器:理论和实践》一书;第二部分是滤镜的初始化方法和饱和度强度设置方法。分别来说。

饱和度片段着色器

饱和度是用来表示颜色的亮度强度的术语。一件亮红色的毛衣的饱和度要远比北京雾霾时灰色的天空的饱和度高得多。
在这个着色器上,参照人类对颜色和亮度的感知过程,有一些优化可以使用。一般而言,人类对亮度要比对颜色敏感的多。这么多年来,压缩软件体积的一个优化方式就是减少存储颜色所用的内存。
人类不仅对亮度比颜色要敏感,同样亮度下,我们对某些特定的颜色反应也更加灵敏,尤其是绿色。这意味着,当你寻找压缩图片的方式,或者以某种方式改变它们的亮度和颜色的时候,多放一些注意力在绿色光谱上是很重要的,因为我们对它最为敏感。

//饱和度片元着色器
NSString *const kGPUImageSaturationFragmentShaderString = SHADER_STRING
(
 varying highp vec2 textureCoordinate;
 
 uniform sampler2D inputImageTexture;
 uniform lowp float saturation;
 
 // Values from "Graphics Shaders: Theory and Practice" by Bailey and Cunningham
 const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);
 
 void main()
 {
    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
    lowp float luminance = dot(textureColor.rgb, luminanceWeighting);
    lowp vec3 greyScaleColor = vec3(luminance);
    
    gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);
     
 }
);

在 GLSL 中,有三种标签可以赋值给我们的变量:

  • Uniforms
  • Attributes
  • Varyings

Uniforms 是一种外界和你的着色器交流的方式,在顶点着色器和片段着色器里都可以被访问到。Uniforms 在一个渲染循环里作为不变的输入值。如果你正在应用茶色滤镜,并且你已经指定了滤镜的强度,那么这些就是在渲染过程中不需要改变的事情,你可以把它作为 Uniform 输入。
Attributes是随顶点位置不同会变的输入值
Varying 是用来在顶点着色器和片段着色器传递信息的,在顶点着色器和片段着色器都会出现,并且在顶点着色器和片段着色器中必须有匹配的名字。数值在顶点着色器被写入到 varying ,然后在片段着色器被读出。被写入 varying 中的值,在片段着色器中会被以插值的形式插入到两个顶点之间的各个像素中去。
这里出现了两种,逐行来看

varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float saturation;
  • textureCoordinate:因为顶点着色器负责和片段着色器交流,所以需要创建一个变量和它共享相关的信息。在图像处理中,片段着色器需要的唯一相关信息就是顶点着色器现在正在处理哪个像素,它需要存储像素的 X 和 Y 坐标。片段着色器接收到的正是当前在顶点着色器被设置好的纹理坐标。所以为输入纹理坐标和输入图片纹理声明一个varyings变量。
  • inputImageTexture:为了处理图像,从应用中接收一个图片的引用,把它当做一个 2D 的纹理。这个数据类型被叫做 sampler2D ,这是因为要从这个 2D 纹理中采样出一个点来进行处理。
  • saturation :饱和度的数值是一个我们从用户界面设置的参数。我们需要知道用户需要多少饱和度,从而展示正确的颜色数量。
const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);

这就是设置三个元素的向量,为亮度来保存颜色比重的地方。这三个值加起来要为 1,这样我们才能把亮度计算为 0.0 - 1.0 之间的值。注意中间的值,就是表示绿色的值,用了 70% 的颜色比重,而蓝色只用了它的 10%。蓝色对我们的展示不是很好,把更多权重放在绿色上是很有意义的。

接下来是main()函数中的代码:

lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
lowp float luminance = dot(textureColor.rgb, luminanceWeighting);
lowp vec3 greyScaleColor = vec3(luminance);

  • textureColor:texture2D()是GLSL 特有的方法,顾名思义,创建一个 2D 的纹理。它采用之前声明过的属性作为参数来决定被处理的像素的颜色,取样特定像素在图片/纹理中的具体坐标来获取颜色信息。
  • luminance:dot()这是在使用 GLSL 中的点乘运算。点乘计算以包含纹理颜色信息的 vec4 为参数,舍弃 vec4 的最后一个不需要的元素,将它和相对应的亮度权重相乘。然后取出所有的三个值把它们加在一起,计算出这个像素综合的亮度值。
  • greyScaleColor:创建一个三个值都是亮度信息的 vec3。如果只指定一个值,编译器会帮你把该将向量中的每个分量都设成这个值。
gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);

把所有的片段组合起来。为了确定每个新的颜色是什么,使用mix() 函数。mix 函数会把刚刚计算的灰度值初始的纹理颜色以及得到的饱和度信息相结合。因为片段着色器的唯一目的就是确定一个像素的颜色,gl_FragColor 本质上就是片段着色器的返回语句。一旦这个片段的颜色被设置,接下来片段着色器就不需要再做其他任何事情了,所以在这之后写任何的语句,都不会被执行。

几个函数:

mix(): mix 函数将两个值 (例如颜色值) 混合为一个变量。如果我们有红和绿两个颜色,我们可以用 mix() 函数线性插值。这在图像处理中很常用,比如在应用程序中通过一组独特的设定来控制效果的强度等。

GPUImageSaturationFilter: @implementation

GPUImageSaturationFilter只有两个方法,一个init方法和一个- (void)setSaturation:(CGFloat)newValue;方法。

- (id)init {
    if (!(self = [super initWithFragmentShaderFromString:kGPUImageSaturationFragmentShaderString])) {
        return nil;
    }
    saturationUniform = [filterProgram uniformIndex:@"saturation"];
    self.saturation = 1.0;

    return self;
}
- (void)setSaturation:(CGFloat)newValue {
    _saturation = newValue;
    [self setFloat:_saturation forUniform:saturationUniform program:filterProgram];
}
  • init方法:把创建的片段着色器kGPUImageSaturationFragmentShaderString通过父类GPUImageFilter初始化
    saturationUniform = [filterProgram uniformIndex:@"saturation"]// This does assume a name of "saturation" for the fragment shader//得到名字叫“ saturation”的片元着色器常量

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容