The purpose of this case is to understand how to use GLSL to achieve grayscale + upside down + Mosaic (total 5 kinds) filter

The overall effect is as follows:

  • The code for the preparation is the same as in OpenGL ES example 11: Split Filter. You only need to change the corresponding bottom item array, the corresponding shader name, etc., which is not covered here
  • Vertex shaders also do not have any changes, mainly the implementation filter algorithm in the slice shader

The GLSL code in the filter’s custom chip shader is described below

Gray filter

The realization principle of gray filter is to make RGB value maintain a balance and fill, or only retain a brightness value, namely green, in the eyes of people, the brightness of green is the most conspicuous, the darker the green value, the darker the picture in the naked eye, this is a physiological phenomenon of the eye.

There are 5 kinds of algorithms for gray filter, which can be roughly divided into 3 categories

  • Weight method: the image after processing is more realistic
    • Floating point algorithm:Gray = R*0.3 + G*0.59 + B*0.11(The sum of RGB weights is 1)
    • Integer method:Gray = (R*30 + G*59 + B*11) /100(The sum of RGB weights is 100)
    • Shift method:Gray = (R*76 + G*151 + B*28) >>8
  • Average value method:Gray = (R+G+B) /3, the image after processing is softer
  • Green only:Gray = G, this way is convenient and simple, and easy to use

Floating-point algorithm and green-only algorithm are used to implement grayscale filter in the chip shader.

  • Floating point algorithm: The RGB weights here are taken from the GPUImage framework
precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; Const highp vec3 W = vec3(0.2125, 0.7154, 0.0721); Void main(){vec4 mask = texture2D(Texture, TextureCoordsVarying); vec4 mask = texture2D(Texture, TextureCoordsVarying); Float luminance = dot(mask.rgb, W); float luminance = dot(mask.rgb, W); / / converts gray value (luminance, luminance, luminance mask. A) and populate into pixels gl_FragColor = vec4 (vec3 (luminance), 1.0); }Copy the code
  • Just take the green
precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; Void main(){vec4 mask = texture2D(Texture, TextureCoordsVarying); vec4 mask = texture2D(Texture, TextureCoordsVarying); Gl_FragColor = vec4(mask.g, mask.g, mask.g, 1.0); }Copy the code

The renderings are shown below

In addition to custom shaders, grayscale filters can also be implemented through GPUImage and iOS native CoreImage. Which one you choose depends on your needs.

Inverted filter

Speaking of upside-down images, the previous post on OpenGL ES GLSL Image Upside-down solution (6 ways) explains several ways to flip an image upside-down. The image was originally upside-down, why is it still upside-down? The reason is that if the image is inverted in the first place, it will affect the display of other filter effects. The inverted filter is only one filter effect and will not affect other filter effects.

Invert the filter by reversing the texture coordinate y value in the chip shader

precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; Void main(){vec4 mask = texture2D(Texture, vec2(TextureCoordsVarying. X, 1.0-texturecoordsVARYING. Y)); Gl_FragColor = vec4 (mask. RGB, 1.0); }Copy the code

Mosaic filter

The main is to achieve different Mosaic style filter, there are mainly the following three styles

  • A square
  • hexagon
  • triangle

Different Mosaic styles correspond to different filter algorithms, as shown in the figure

Square Mosaic

Square Mosaic principle: To represent a fairly large area of an image with the same color value is to reduce the resolution of the image on a large scale, so that some of the details of the image are hidden

The filter algorithm mainly has the following steps:

  • Calculate the position in the actual image according to the texture coordinates, which is equivalent to magnifying the texture color area

  • Calculate the coordinates of a small Mosaic, that is, find the pixels of the Mosaic to extract the color values

  • Convert the Mosaic coordinates back to the texture coordinates, reducing the texture color area as shown in the figure

The filter algorithm in chip coloring is implemented as follows

precision highp float; // Texture coordinate uniform sampler2D Texture; // Texture sampler varying vec2 TextureCoordsVarying; Const vec2 TexSize = vec2(400.0, 400.0); // size const vec2 = vec2(16.0, 16.0); Vec2 intXY = vec2(TextureCoordsVarying. X * texsize.x, TextureCoordsVarying. Y * texsize.y); //floor(x) is a built-in function that returns the largest integer less than/equal to x, //floor(intxy. x/ mosaicsize.x)* mosaicsize.x (floor(intxy. x/ mosaicsize.x)* mosaicsize.x vec2(floor(intXY.x/MosaicSize.x)*MosaicSize.x, floor(intXY.y/MosaicSize.y)*MosaicSize.y); Vec2 UVMosaic = vec2(XYMosaic. X/texsize.x, XYMosaic. Y/texsize.y); Vec4 color = texture2D(Texture, UVMosaic); // Assign the Mosaic color to gl_FragColor gl_FragColor = color; }Copy the code

Hexagonal Mosaic

Hexagon Mosaic principle: will a picture, divided into consists of hexagon, then take each hexagonal center by drawing a rectangular one by one, according to the parity of rectangular arrangement situation and the corresponding two center, and calculate the distance the texture coordinates with the two center, according to distance judgment, adopt principle nearby, the current hexagon used close to the center of the color values.

Divide the image into hexagons and draw a rectangle with the center point of the hexagons as follows

The main steps of filter algorithm are as follows:

  • Set the length to width ratio of the rectangle TR and TB (TB: TR match ratio 3: √3), where the length to width ratio is 3: √3. The calculation process is as follows:

  • Gets the x, y coordinates of the texture

  • Calculate the corresponding rectangular coordinates wx and wy according to the texture coordinates. Suppose the ratio of the matrix is 3*len: √3*len, then the matrix coordinates corresponding to the texture coordinates (x, y) are

  • According to the parity of the row, the corresponding center point texture coordinates v1, V2

    • Even row and even column :(0,0) (1,1) /, i.e. upper left and lower right

    • Even row odd column :(0,1) (1,0) \, i.e. lower left, upper right

    • Odd rows and even columns :(0,1) (1,0) \, i.e. lower left and upper right

    • Odd row and odd column :(0,0) (1,1) /, that is, there are only two cases when the top left and bottom right are finally summed up, (0,0) (1,1) and (0,1) (1,0), as shown in the figure below

In a single matrix, the coordinate calculation formula of 4 points is as follows:

  • For wx+1 in the calculation, for the point (1, 0), wx+1 is the same as the length of a rectangle between (1, 0) and (0,0), which is 1. In order to get the coordinates of (1, 0), we need to increase wx by one length based on the coordinates of (0,0)
  • For wy+1 in the calculation, for the point (0,1), wy+1 is the same as the height of the rectangle between (0,0) and (0,1). This length is 1. To get the coordinates of (0,1), we need to increase wy by a height based on the coordinates of (0,0)

  • The distance s1 and S2 between the pixel and the two center points are calculated according to the distance formula

    • S1 = √ ((v1.x-x) ² + (v1.y-y) ²)

    • S2 = √ ((v2.x-x) ² + (v2.y-y) ²)

  • According to the calculated distance, to judge which center point is near, the color value of the center point of the hexagon is taken as the color value of the hexagon

Chip shader code

precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; // Const float mosaicSize = 0.03; void main(){ float length = mosaicSize; Float TR = 0.866025; float TR = 0.866025; float TR = 0.866025; // Float TB = 1.5; // Float TB = 1.5; Float x = TextureCoordsVarying. X; float y = TextureCoordsVarying.y; Wx = int (texture coordinate x/ matrix length), matrix length = TB*len (texture coordinate y/ matrix width), wx = int (texture coordinate y/ matrix width), TR*len int wx = int(x/TB/length); int wy = int(y / TR / length); vec2 v1, v2, vn; // Check whether wx is even, Equivalent to wx % 2 = = 0 if (wx / 2 * 2 = = wx) {if (wy / 2 * 2 = = wy) {/ / I line I columns / / (0, 0), (1, 1) v1 = vec2 (length * * float of TB (wx), length * TR * float(wy)); v2 = vec2(length * TB * float(wx+1), length * TR * float(wy+1)); } else {/ / I / / columns, rows of (0, 1), (1, 0) v1 = vec2 (length * * float of TB (wx), length * TR * float (wy + 1)); v2 = vec2(length * TB * float(wx+1), length * TR * float(wy)); }} else {the if (wy / 2 * 2 = = wy) {/ / odd lines even columns / / (0, 1), (1, 0) v1 = vec2 (length * * float of TB (wx), length * TR * float (wy + 1)); v2 = vec2(length * TB * float(wx+1), length * TR * float(wy)); //(0,0),(1,1) v1 = vec2(length * TB * float(wx), length * TR * float(wy)); v2 = vec2(length * TB * float(wx+1), length * TR * float(wy+1)); Float s1 = SQRT (pow(v1.x-x, 2.0) + pow(v1.y-y, 2.0)); float s1 = SQRT (pow(v1.x-x, 2.0) + pow(v1.y-y, 2.0)); Float s2 = SQRT (pow (v2. X - x 2.0) + pow (2.0) v2. - y, y); Vn = (s1 < s2)? Vn = (s1 < s2)? Vn = (s1 < s2)? v1 : v2; Vec4 = texture2D(Texture, vn); vec4 = texture2D(Texture, vn); gl_FragColor = color; }Copy the code

Triangular Mosaic

Principle: Triangle Mosaic evolved by hexagon Mosaic, get the premise of the triangle, is the first hexagon, then six equal hexagon, each triangle is a triangle, and then take the texture coordinates with the center point of the Angle, and the center of the triangle at the same time, according to the Angle of judgment, belongs to which Angle triangle, The color of the center point of the triangle is the texture of the entire triangle

The triangular filter algorithm step is to add the following steps to the hexagonal filter algorithm step:

  • Figure out the included Angle between the current pixel point and the texture center point. The texture coordinate is (x, y) and the center point is vn. Figure out the included Angle

  • Calculate the center points of the six triangles

  • To determine which triangle the included Angle belongs to, the coordinate of the center point of the triangle is obtained. Among them, the range of the included Angle of different triangles is shown in the figure

Chip shader code: in the hexagon filter algorithm (i.e. Vn = (s1 < s2)? V1, v2) Add the following code

/ / get pixel point and the Angle of the center of float a = atan ((x -.vn. X)/(y -.vn. Y)); Vec2 area1 = vec2(vn.x, vN. y - mosaicSize * TR / 2.0); vec2 area1 = vec2(vn.x, vN. y - mosaicSize * TR / 2.0); Vec2 area2 = vec2(vn.x + mosaicSize / 2.0, vN. y - mosaicSize * TR / 2.0); Vec2 area3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0); Vec2 area4 = vec2(vn.x, vn.y + mosaicSize * TR / 2.0); Vec2 area5 = vec2(vn. x-mosaicsize / 2.0, vn.y + mosaicSize * TR / 2.0); Vec2 area6 = vec2(vn. x-mosaicsize / 2.0, vN. y-mosaicsize * TR / 2.0); If (a >= PI6 && a < PI6 * 3.0) {vn = area1; }else if (a >= PI6 * 3.0 && a < PI6 * 5.0){vn = area2; } else if (a > = PI6 * && a < = 5.0 PI6 * 6.0) | | (a < - PI6 * 5.0 && a > - PI6 * 6.0)) {.vn = area3; }else if (a < -pi6 * 3.0 && a >= -pi6 * 5.0){vn = area4; }else if (a <= -pi6 && a > -pi6 * 3.0){vn = area5; }else if (a > -PI6 && a < PI6){ vn = area6; Vec4 color = texture2D(Texture, vn); vec4 Color = texture2D(Texture, vn); // Fill the color value into the chip shader built-in variable gl_FragColor gl_FragColor = color;Copy the code

Atan is a built-in function in GLSL, which can be calculated in two ways: 1. Atan (y,x) has a range of [0, π]; 2. Atan (y/x) has a range of [-π/2, π/2].

The complete code is Github – 14_ grayscale invert Mosaic OC, 14 grayscale invert Mosaic _Swift