滤镜Shader——独立像素点变换

独立像素点变换,它是针对单个像素点的操作,常见的效果包括亮度、对比、饱和度、色调等。

在之前学习OpenGL的时候已经实现和描述了这几个效果滤镜OpenGL简单滤镜,这里就不在展开介绍了,接下来让我们用Metal实现一下上述的效果。

Metal实现的滤镜处理框架——MetalImage

饱和度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <metal_stdlib>
using namespace metal;

struct SingleInputVertexIO {
float4 position [[position]];
float2 textureCoordinate [[user(texturecoord)]];
};

constant half3 luminanceWeighting = half3(0.2125, 0.7154, 0.0721);

#pragma mark - 饱和度
fragment half4 saturationFragment(SingleInputVertexIO fragmentInput [[stage_in]],
constant float &saturation [[buffer(2)]],
texture2d<half> inputTexture [[texture(0)]]) {
constexpr sampler quadSampler;

half4 textureColor = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
half luminance = dot(textureColor.rgb, luminanceWeighting);
half3 greyScaleColor = half3(luminance);
half4 color = half4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);

return color;
}

对比度

1
2
3
4
5
6
7
8
9
#pragma mark - 对比度
fragment half4 contrastFragment(SingleInputVertexIO fragmentInput [[stage_in]],
constant float &contrast [[buffer(2)]],
texture2d<half> inputTexture [[texture(0)]]) {
constexpr sampler quadSampler;
half4 textureColor = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
half4 color = half4(((textureColor.rgb - half3(0.5)) * contrast + half3(0.5)), textureColor.w);
return color;
}

亮度

1
2
3
4
5
6
7
8
9
10
11
12
13
#pragma mark - 亮度
fragment half4 luminanceRangeFragment(SingleInputVertexIO fragmentInput [[stage_in]],
constant float &rangeReduction [[buffer(2)]],
texture2d<half> inputTexture [[texture(0)]]) {
constexpr sampler quadSampler;

half4 textureColor = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
half luminance = dot(textureColor.rgb, luminanceWeighting);
half luminanceRatio = ((0.5 - luminance) * rangeReduction);
half4 color = half4((textureColor.rgb) + (luminanceRatio), textureColor.w);

return color;
}

色调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#pragma mark - 色调
constant half4 kRGBToYPrime = half4(0.299, 0.587, 0.114, 0.0);
constant half4 kRGBToI = half4(0.595716, -0.274453, -0.321263, 0.0);
constant half4 kRGBToQ = half4(0.211456, -0.522591, 0.31135, 0.0);

constant half4 kYIQToR = half4(1.0, 0.9563, 0.6210, 0.0);
constant half4 kYIQToG = half4(1.0, -0.2721, -0.6474, 0.0);
constant half4 kYIQToB = half4(1.0, -1.1070, 1.7046, 0.0);

fragment half4 hueFragment(SingleInputVertexIO fragmentInput [[stage_in]],
constant float &hueAdjust [[buffer(2)]],
texture2d<half> inputTexture [[texture(0)]]) {
constexpr sampler quadSampler;
half4 textureColor = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);

// Convert to YIQ
half YPrime = dot(textureColor, kRGBToYPrime);
half I = dot(textureColor, kRGBToI);
half Q = dot(textureColor, kRGBToQ);

// Calculate the hue and chroma
half hue = atan2(Q, I);
half chroma = sqrt(I * I + Q * Q);

// Make the user's adjustments
hue += (-hueAdjust);

// Convert back to YIQ
Q = chroma * sin (hue);
I = chroma * cos (hue);

// Convert back to RGB
half4 yIQ = half4(YPrime, I, Q, 0.0);
textureColor.r = dot(yIQ, kYIQToR);
textureColor.g = dot(yIQ, kYIQToG);
textureColor.b = dot(yIQ, kYIQToB);

return textureColor;
}