Implementation effect
1. Fuzzy mean value
The so-called fuzzy, is to let the image is not clear, so let the picture is not clear what methods
-
Reduce image
It is easy to understand how to shrink the image. When we reduce the original 1080*960 image to 540*480, that is, half of the original size, but when we display it, we still let it display in 1080*960 size, at this time the image looks more blurred than the original
-
The pixel is the average of the surrounding pixels
When a picture of a person is very hard to see, we might say that the person’s eyes and nose are plastered together, which means that the pixels are smooth, rather than sharp, as in hd
Below, we are using both methods to blur the image
The pixel is the average of the surrounding pixels
Look at the table below
If this form of value is the value of the pixel, so as you can see, the center pixel value is 2, the surrounding pixels value is 1 (of course, these values are the custom, you can also customize other values), the next thing to do to the center pixel blur, using fuzzy mean value, will add up all the pixels, then divided by the total number, the result is in the end
As you can see, the pixel value of the center point is changed from 2 to 1, which completes the blur of one pixel. For the entire graph, you need to traverse every element and do the same for each element
In fact, blurRadius and blurOffset are also involved in the operation of blur. In the picture above, the blurRadius is 1, and the blur step is also 1. The larger the blurRadius and blur step are, the blurrier the image will be
OpenGLES implementation
Next, OpenGLES is used to implement mean blur
As we all know, OpenGLES ‘chip shader deals with each pixel, so you can actually blur each pixel in the chip shader
Vertex shader
attribute vec4 aPos;
attribute vec2 aCoordinate;
varying vec2 vCoordinate;
void main(a){
vCoordinate = aCoordinate;
gl_Position = aPos;
}
Copy the code
Vertex shaders are more conventional and do nothing special
Chip shader
precision mediump float;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
uniform int uBlurRadius;
uniform vec2 uBlurOffset;
// Boundary value processing
vec2 clampCoordinate(vec2 coordinate) {
return vec2(clamp(coordinate.x, 0.0.1.0), clamp(coordinate.y, 0.0.1.0));
}
void main(a){
vec4 sourceColor = texture2D(uSampler, vCoordinate);
if (uBlurRadius <= 1) {
gl_FragColor = sourceColor;
return;
}
vec3 finalColor = sourceColor.rgb;
for (int i = 0; i < uBlurRadius; i++) {
finalColor += texture2D(uSampler, clampCoordinate(vCoordinate + uBlurOffset * float(i))).rgb;
finalColor += texture2D(uSampler, clampCoordinate(vCoordinate - uBlurOffset * float(i))).rgb;
}
finalColor /= float(2 * uBlurRadius + 1);
gl_FragColor = vec4(finalColor, sourceColor.a);
}
Copy the code
Chip shaders are relatively complex, so let’s take our time
There are two uniform variables, which are
-
uBlurRadius
A variable of type int that represents the blur radius
-
uBlurOffset
A vec2 type variable, with two components x and y, represents the horizontal and vertical fuzzy steps
The clampCoordinate function is custom defined in the chip shader. This function is used to keep the texture coordinate within the range of 0 to 1
Now let’s look at the main function
-
vec4 sourceColor = texture2D(uSampler, vCoordinate);
Gets the pixel value of the current sampling point
-
Determine whether the blur radius is less than or equal to 1. If yes, do not blur and return directly
-
Create a new VEC4 variable, finalColor, to represent the final pixel value
-
The for loop retrieves the surrounding pixel values based on the blur radius and blur step size
-
FinalColor eventually needs to be divided by the number of pixels
Through the above steps, we have completed the mean blur
For those of you who may have noticed that I only did one for loop here, there should be two nested for loops
for (int i = 0; i < uBlurRadius; i++) {
for (int j = 0; j < uBlurRadius; j++) { ...... }}Copy the code
UBlurOffset = VEC2 (blurOffsetW, 0.0), vec2(blurOffsetW, 0.0); UBlurOffset = VEC2 (0.0, blurOffsetH)
If you use two for loops, the total number of loops will be uBlurRadius * uBlurRadius. If you use two for loops, the total number of loops will be uBlurRadius + uBlurRadius. Rendering efficiency can be greatly improved, especially when the blur radius is large
You can check out MeanBlurFilter on GitHub
Implementation effect
uBlurRadius = 30
uBlurOffset = 1
Note that the blur effect is mediocre when we increase the blur step to
uBlurRadius = 30
uBlurOffset = 5
Then the following results are obtained
Ah, it took a long time, and here it is, here it is.
Emmm, don’t panic, let’s analyze why this result appears first
When uBlurOffset is equal to 1, the effect is not so good, and when uBlurOffset is equal to 5, the effect is even worse, because
The closer the peripheral pixels are to the current pixel, the greater the difference between them. However, when calculating, we did not take this situation into account and directly calculated the average value, which resulted in a poor rendering effect
So, is there a way to solve this? Well, of course there is. Let’s first understand the normal distribution
2. Normal distribution
Normal distribution, also known as “Normal distribution”, also known as Gaussian distribution, was first obtained by Abraham de Moivre in the asymptotic formula of binomial distribution. C.F. Gauss derived it from another perspective while studying measurement errors. P.S. Laplace and gauss studied its properties. Is a probability distribution that is very important in mathematics, physics, and engineering, and has great influence in many aspects of statistics.
The important thing is the formula for the normal distribution, the formula for the density of the normal distribution (μ = 0, the mean is 0)
Normally distributed image
So now that we have this formula, what do we do
Remember the blur radius, the blur radius is the range of x, for example, uBlurRadius = 10, then x is in the range of 0 to 9
The larger the value of x is, the smaller the final calculation result will be. In other words, the farther away from the central pixel, the smaller the relationship will be, and vice versa
Careful people have discovered that the formula contains a variable called sigma, which actually represents the standard deviation
Standard Deviation (Standard Deviation) is the arithmetic square root of the arithmetic mean from the square of the mean (i.e., the variance), expressed as σ. Standard deviation is also known as standard deviation, or experimental standard deviation, and is most commonly used in probability statistics as a measure of the degree of statistical distribution.
Standard deviation is the arithmetic square root of variance. Standard deviation reflects the degree of dispersion of a data set. Two sets of data with the same mean may not have the same standard deviation.
From the point of view of the image, the bigger σ, the flatter the image of the normal distribution, the smaller σ, the normal distribution will be concentrated in the center, and the higher
Let’s start with the implementation of Gaussian blur and ground-glass
Gaussian blur
The normal distribution, also known as the Gaussian distribution, using the density function of the normal distribution to do fuzzy processing, is called the Gaussian blur
In the previous mean blur, we added the surrounding pixels and averaged them. The Gaussian blur also needs to add and average the surrounding pixels, but with a weighted average
- First of all, according to the normally distributed
Density function
And calculate all the values within the blur radiusThe weight
- Because to make sure that the ownership readds to one, you need to divide each weight into one
The total weight
- When calculating fuzziness, the sampled pixel value needs to be multiplied by the corresponding
The weight
OpenGLES implementation
Vertex shader
attribute vec4 aPos;
attribute vec2 aCoordinate;
varying vec2 vCoordinate;
void main(a){
vCoordinate = aCoordinate;
gl_Position = aPos;
}
Copy the code
Like mean fuzziness, vertex shaders are not treated in any special way
Chip shader
precision mediump float;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
// Blur radius
uniform int uBlurRadius;
// Blur the step size
uniform vec2 uBlurOffset;
/ / total weight
uniform float uSumWeight;
// PI
const float PI = 3.1415926;
// Boundary value processing
vec2 clampCoordinate(vec2 coordinate) {
return vec2(clamp(coordinate.x, 0.0.1.0), clamp(coordinate.y, 0.0.1.0));
}
// Calculate the weight
float getWeight(int i) {
float sigma = float(uBlurRadius) / 3.0;
return (1.0 / sqrt(2.0 * PI * sigma * sigma)) * exp(-float(i * i) / (2.0 * sigma * sigma)) / uSumWeight;
}
void main(a){
vec4 sourceColor = texture2D(uSampler, vCoordinate);
if (uBlurRadius <= 1) {
gl_FragColor = sourceColor;
return;
}
float weight = getWeight(0);
vec3 finalColor = sourceColor.rgb * weight;
for (int i = 1; i < uBlurRadius; i++) {
weight = getWeight(i);
finalColor += texture2D(uSampler, clampCoordinate(vCoordinate - uBlurOffset * float(i))).rgb * weight;
finalColor += texture2D(uSampler, clampCoordinate(vCoordinate + uBlurOffset * float(i))).rgb * weight;
}
gl_FragColor = vec4(finalColor, sourceColor.a);
}
Copy the code
At first glance, the pixel shader for Gaussian blur looks about the same as the pixel shader for mean blur. If you read it carefully, you will find that there is a slight difference in the for loop, which is the pixel value sampled, multiplied by the corresponding weight
It should be noted that the GLSL, cannot pass us array, and when we need to change the blur radius, have to calculate weight gaussian blur, so here the author is divided into two parts, the Java part according to the fuzzy radius calculated total weight value to GLSL, fragment shader, according to the for loop, calculate the corresponding weight value, That would satisfy our needs
The calling code is not posted here, but you can view GaussianBlurFilter on GitHub
Implementation effect
uBlurRadius = 30
uBlurOffset = 1
As you can see, the Gaussian blur looks more natural and better than the mean blur
Gaussian blur, so how to achieve the ground-glass effect, in fact, only need to modify some parameters, such as increasing the blur radius, blur step size
Four, frosted glass
With gaussian blur implemented above, we can achieve the frosted glass effect by increasing the blur step, for example
uBlurRadius = 30
uBlurOffset = 5
Of course, we can do this by increasing the blur radius
So the question is, can the blur radius always be increased? The answer is no, because depending on the performance of different models, if you increase the blur radius too much, the picture will be stuck, especially when rendering in real time
So is there a solution? The answer is yes. We can reduce the texture size by several times before we blur it, because the blur itself does not need the details of the image. When we shrink it, the blur effect will not be affected
GitHub
Welcome to Star MediaDemo