GPUImage濾鏡中的shader代碼分析,及自定義濾鏡

GPUImage由於使用GPU,顧其在濾鏡染色的時候真正使用的是Open GL的shader語言。下面我們就GPUImagePinchDistortionFilter分析這些shader語言。

GPUImagePinchDistortionFilter是一個實現收縮失真,凹面鏡效果濾鏡,頭文件代碼如下:

#import "GPUImageFilter.h"

/** Creates a pinch distortion of the image
 */
@interface GPUImagePinchDistortionFilter : GPUImageFilter
{
    GLint aspectRatioUniform, radiusUniform, centerUniform, scaleUniform;
}

/** The center about which to apply the distortion, with a default of (0.5, 0.5)
 */
@property(readwrite, nonatomic) CGPoint center;
/** The radius of the distortion, ranging from 0.0 to 2.0, with a default of 1.0
 */
@property(readwrite, nonatomic) CGFloat radius;
/** The amount of distortion to apply, from -2.0 to 2.0, with a default of 0.5
 */
@property(readwrite, nonatomic) CGFloat scale;

@end

我們來分析一下。

首先,這個類必須繼承於GPUImageFilter(這個必須,要不然就沒法用)。

其次,這個類有三個參數:center表示效果所在圓的中心點,radius表示所在圓的半徑(注意範圍是-2.0到2.0),scale表示縮放倍數(當scale小於0的時候,中間縮小,周圍放大;當scale大於0的時候,中間放大,周圍縮小)。

最後,我們需要關注一個單位(這個很重要,要不然後面的shader代碼就很迷茫了)。細心的朋友肯定會發現圓半徑的範圍是-2.0到2.0。很顯然,這不是像素單位,要不然這了濾鏡效果範圍也太小了,和沒有效果一樣)。事實上,在shader語言中,半徑是真實半徑除以圖片矩形的寬(橫着圖片的爲高),記realRadio/targetWidth(橫着的圖片爲realRadio/targetHeight);


源文件中,我們重點分析shader語句kGPUImagePinchDistortionFragmentShaderString。在這裏,我直接加註釋。

NSString *const kGPUImagePinchDistortionFragmentShaderString = SHADER_STRING
(
 varying highp vec2 textureCoordinate;/*變形後的圖片的座標*/
 
 uniform sampler2D inputImageTexture;/*需要變形的圖片*/
 
 uniform highp float aspectRatio;/*座標變換的係數,在shader座標系中width永遠是1.0,height做相應的等比縮放*/
 uniform highp vec2 center;/*參數,中心點*/
 uniform highp float radius;/*參數,圓半徑*/
 uniform highp float scale;/*參數,縮放倍數*/
 
 void main()
 {
     highp vec2 textureCoordinateToUse = vec2(textureCoordinate.x, (textureCoordinate.y * aspectRatio + 0.5 - 0.5 * aspectRatio));/*計算可以用於計算到中心距離的座標*/
     highp float dist = distance(center, textureCoordinateToUse);/*計算到中心的距離*/
     textureCoordinateToUse = textureCoordinate;/*將座標恢復到shader座標系*/
     
     if (dist < radius)/*在圓內*/
     {
         textureCoordinateToUse -= center;/*需要相對中心的座標*/
         highp float percent = 1.0 + ((0.5 - dist) / 0.5) * scale;/*縮放的程度*/
         textureCoordinateToUse = textureCoordinateToUse * percent;/*座標縮放*/
         textureCoordinateToUse += center;/*需要絕對座標*/
         
         gl_FragColor = texture2D(inputImageTexture, textureCoordinateToUse );/*gl_FragColor表示變形後坐標textureCoordinate的顏色*/
</span>     }
     else/*不再園內*/
     {
         gl_FragColor = texture2D(inputImageTexture, textureCoordinate );/*gl_FragColor表示變形後坐標textureCoordinate的顏色*/
     }
 }
);

上面我在shader中加了註釋,實際的shader代碼不要加註釋(因爲代碼長度佔GPU內存,且中文可能會出問題)。

從上面我們可以到,我們可以通過修改第三方的庫來增加自己的濾鏡。事實上,GPUImage已經提供了濾鏡擴展的接口,我們不需要修改它的庫。類GPUImageFilter有一個初始化函數initWithFragmentShaderFromFile:可以加載自定義以fsh爲後綴文件中的shader代碼。例如:

GPUImageFilter *filter1 = [[[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"Shader1"] autorelease];

而Shader.fsh的代碼爲:

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

void main()
{
    lowp vec3 tc = vec3(1.0, 0.0, 0.0);

    lowp vec3 pixcol = texture2D(inputImageTexture, textureCoordinate).rgb;
    lowp vec3 colors[3];
    colors[0] = vec3(0.0, 0.0, 1.0);
    colors[1] = vec3(1.0, 1.0, 0.0);
    colors[2] = vec3(1.0, 0.0, 0.0);
    mediump float lum = (pixcol.r + pixcol.g + pixcol.b) / 3.0;
    int ix = (lum < 0.5)? 0:1;
    tc = mix(colors[ix], colors[ix + 1], (lum - float(ix) * 0.5) / 0.5);

    gl_FragColor = vec4(tc, 1.0);
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章