按照GPUImage(一):視頻采集GPUImageVideoCamera提到的示例demo FilterShowcase的路徑掂林,初始化videoCamera
后設(shè)置GPUImageFilter臣缀。
GPUImageFilter
GPUImageFilter繼承自GPUImageOutput,遵循GPUImageInput協(xié)議泻帮,遵循這個(gè)協(xié)議的對(duì)象都可以從響應(yīng)鏈的上游接收處理過的紋理并繼續(xù)處理精置,下游的處理對(duì)象稱為上一步的target,響應(yīng)鏈的下游可以有多個(gè)target(或分支)锣杂。
所以脂倦,GPUImageFilter就是用來接收源圖像,通過自定義的頂點(diǎn)蹲堂、片元著色器來渲染新的圖像狼讨,并在繪制完成后通知響應(yīng)鏈的下一個(gè)對(duì)象,既可以流入數(shù)據(jù)柒竞,也可以流出數(shù)據(jù)政供,GPUImageView,GPUImageMovieWriter是最終輸出target,來顯示圖片或者視頻朽基。
GPUImageSaturationFilter
直接看GPUImageFilter代碼有些抽象布隔,從GPUImageSaturationFilter入手更容易理解。當(dāng)從FilterShowcase demo列表中選中Saturation時(shí)稼虎,filter操作——
filter = [[GPUImageSaturationFilter alloc] init];//filter初始化
.
.
[videoCamera addTarget:filter];//GPUImageVideoCamera添加filter作為target
.
.
GPUImageView *filterView = (GPUImageView *)self.view;//設(shè)置當(dāng)前view為預(yù)覽view
.
.
[filter addTarget:filterView];//filter添加預(yù)覽view為最終target
.
.
[videoCamera startCameraCapture];//相機(jī)開始捕獲
如果需要更改強(qiáng)度
[(GPUImageSaturationFilter *)filter setSaturation:[(UISlider *)sender value]]; break;
簡(jiǎn)單幾步衅檀,即完成了調(diào)用和強(qiáng)度設(shè)置。
GPUImageSaturationFilter的.h
文件只有一個(gè)saturation屬性用于修改霎俩,.m
文件分為兩個(gè)部分哀军,第一部分是一個(gè)做飽和度調(diào)節(jié)的片段著色器,這個(gè)著色器出自《圖形著色器:理論和實(shí)踐》一書打却;第二部分是濾鏡的初始化方法和飽和度強(qiáng)度設(shè)置方法杉适。分別來說。
飽和度片段著色器
飽和度是用來表示顏色的亮度和強(qiáng)度的術(shù)語柳击。一件亮紅色的毛衣的飽和度要遠(yuǎn)比北京霧霾時(shí)灰色的天空的飽和度高得多猿推。
在這個(gè)著色器上,參照人類對(duì)顏色和亮度的感知過程捌肴,有一些優(yōu)化可以使用蹬叭。一般而言藕咏,人類對(duì)亮度要比對(duì)顏色敏感的多。這么多年來秽五,壓縮軟件體積的一個(gè)優(yōu)化方式就是減少存儲(chǔ)顏色所用的內(nèi)存孽查。
人類不僅對(duì)亮度比顏色要敏感,同樣亮度下筝蚕,我們對(duì)某些特定的顏色反應(yīng)也更加靈敏卦碾,尤其是綠色。這意味著起宽,當(dāng)你尋找壓縮圖片的方式洲胖,或者以某種方式改變它們的亮度和顏色的時(shí)候,多放一些注意力在綠色光譜上是很重要的坯沪,因?yàn)槲覀儗?duì)它最為敏感绿映。
//飽和度片元著色器
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 中,有三種標(biāo)簽可以賦值給我們的變量:
- Uniforms
- Attributes
- Varyings
Uniforms 是一種外界和你的著色器交流的方式腐晾,在頂點(diǎn)著色器和片段著色器里都可以被訪問到叉弦。Uniforms 在一個(gè)渲染循環(huán)里作為不變的輸入值。如果你正在應(yīng)用茶色濾鏡藻糖,并且你已經(jīng)指定了濾鏡的強(qiáng)度淹冰,那么這些就是在渲染過程中不需要改變的事情,你可以把它作為 Uniform 輸入巨柒。
Attributes是隨頂點(diǎn)位置不同會(huì)變的輸入值
Varying 是用來在頂點(diǎn)著色器和片段著色器傳遞信息的樱拴,在頂點(diǎn)著色器和片段著色器都會(huì)出現(xiàn),并且在頂點(diǎn)著色器和片段著色器中必須有匹配的名字洋满。數(shù)值在頂點(diǎn)著色器被寫入到 varying 晶乔,然后在片段著色器被讀出。被寫入 varying 中的值牺勾,在片段著色器中會(huì)被以插值的形式插入到兩個(gè)頂點(diǎn)之間的各個(gè)像素中去正罢。
這里出現(xiàn)了兩種,逐行來看
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float saturation;
- textureCoordinate:因?yàn)轫旤c(diǎn)著色器負(fù)責(zé)和片段著色器交流驻民,所以需要?jiǎng)?chuàng)建一個(gè)變量和它共享相關(guān)的信息翻具。在圖像處理中,片段著色器需要的唯一相關(guān)信息就是頂點(diǎn)著色器現(xiàn)在正在處理哪個(gè)像素回还,它需要存儲(chǔ)像素的 X 和 Y 坐標(biāo)裆泳。片段著色器接收到的正是當(dāng)前在頂點(diǎn)著色器被設(shè)置好的紋理坐標(biāo)。所以為輸入紋理坐標(biāo)和輸入圖片紋理聲明一個(gè)varyings變量懦趋。
- inputImageTexture:為了處理圖像晾虑,從應(yīng)用中接收一個(gè)圖片的引用疹味,把它當(dāng)做一個(gè) 2D 的紋理仅叫。這個(gè)數(shù)據(jù)類型被叫做 sampler2D 帜篇,這是因?yàn)橐獜倪@個(gè) 2D 紋理中采樣出一個(gè)點(diǎn)來進(jìn)行處理。
- saturation :飽和度的數(shù)值是一個(gè)我們從用戶界面設(shè)置的參數(shù)诫咱。我們需要知道用戶需要多少飽和度笙隙,從而展示正確的顏色數(shù)量。
const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);
這就是設(shè)置三個(gè)元素的向量坎缭,為亮度來保存顏色比重的地方竟痰。這三個(gè)值加起來要為 1,這樣我們才能把亮度計(jì)算為 0.0 - 1.0 之間的值掏呼。注意中間的值坏快,就是表示綠色的值,用了 70% 的顏色比重憎夷,而藍(lán)色只用了它的 10%莽鸿。藍(lán)色對(duì)我們的展示不是很好,把更多權(quán)重放在綠色上是很有意義的拾给。
接下來是main()函數(shù)中的代碼:
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
lowp float luminance = dot(textureColor.rgb, luminanceWeighting);
lowp vec3 greyScaleColor = vec3(luminance);
- textureColor:texture2D()是GLSL 特有的方法祥得,顧名思義,創(chuàng)建一個(gè) 2D 的紋理蒋得。它采用之前聲明過的屬性作為參數(shù)來決定被處理的像素的顏色级及,取樣特定像素在圖片/紋理中的具體坐標(biāo)來獲取顏色信息。
- luminance:dot()這是在使用 GLSL 中的點(diǎn)乘運(yùn)算额衙。點(diǎn)乘計(jì)算以包含紋理顏色信息的 vec4 為參數(shù)饮焦,舍棄 vec4 的最后一個(gè)不需要的元素,將它和相對(duì)應(yīng)的亮度權(quán)重相乘入偷。然后取出所有的三個(gè)值把它們加在一起追驴,計(jì)算出這個(gè)像素綜合的亮度值。
- greyScaleColor:創(chuàng)建一個(gè)三個(gè)值都是亮度信息的 vec3疏之。如果只指定一個(gè)值殿雪,編譯器會(huì)幫你把該將向量中的每個(gè)分量都設(shè)成這個(gè)值。
gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);
把所有的片段組合起來锋爪。為了確定每個(gè)新的顏色是什么丙曙,使用mix() 函數(shù)。mix 函數(shù)會(huì)把剛剛計(jì)算的灰度值和初始的紋理顏色以及得到的飽和度信息相結(jié)合其骄。因?yàn)槠沃鞯奈ㄒ荒康木褪谴_定一個(gè)像素的顏色亏镰,gl_FragColor 本質(zhì)上就是片段著色器的返回語句。一旦這個(gè)片段的顏色被設(shè)置拯爽,接下來片段著色器就不需要再做其他任何事情了索抓,所以在這之后寫任何的語句,都不會(huì)被執(zhí)行。
幾個(gè)函數(shù):
mix(): mix 函數(shù)將兩個(gè)值 (例如顏色值) 混合為一個(gè)變量逼肯。如果我們有紅和綠兩個(gè)顏色耸黑,我們可以用 mix() 函數(shù)線性插值。這在圖像處理中很常用篮幢,比如在應(yīng)用程序中通過一組獨(dú)特的設(shè)定來控制效果的強(qiáng)度等大刊。
GPUImageSaturationFilter: @implementation
GPUImageSaturationFilter只有兩個(gè)方法,一個(gè)init
方法和一個(gè)- (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方法:把創(chuàng)建的片段著色器kGPUImageSaturationFragmentShaderString通過父類GPUImageFilter初始化缺菌。
saturationUniform = [filterProgram uniformIndex:@"saturation"]
// This does assume a name of "saturation" for the fragment shader//得到名字叫“ saturation”的片元著色器常量- (void)setSaturation:(CGFloat)newValue:
[self setFloat:_saturation forUniform:saturationUniform program:filterProgram]
調(diào)用父類GPUImageFilter的方法,saturationUniform即為init方法中得到的常量搜锰,filterProgram是父類GPUImageFilter中初始化的GLProgram對(duì)象示例伴郁,GLProgram在后續(xù)文章中再提。
如果實(shí)現(xiàn)更復(fù)雜的濾鏡效果蛋叼,可以參考基于GPUImage的實(shí)時(shí)美顏濾鏡
GPUImage濾鏡中的shader代碼分析蛾绎,及自定義濾鏡
用GPUImage做難一點(diǎn)點(diǎn)的效果
- (void)setSaturation:(CGFloat)newValue: