It is necessary for me to calculate the kernel for two-pass blur. Suppose there is a blur kernel 5x5, which is generated as follows:
public static float[][] getKernelf(int size, float sigma) {
float[][] kernel = new float[size][size];
int mean = size / 2;
float sum = 0; // For accumulating the kernel values
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
kernel[x][y] = (float) (Math.exp(-0.5 * (Math.pow((x - mean) / sigma, 2.0) +
Math.pow((y - mean) / sigma, 2.0))) / (2 * Geometry.PI * sigma * sigma));
// Accumulate the kernel values
sum += kernel[x][y];
}
}
// Normalize the kernel
for (int x = 0; x < size; x++)
for (int y = 0; y < size; y++)
kernel[x][y] /= sum;
return kernel;
}
With sigma = 1 and size = 5 we have the following kernel:
0.0029690173 0.013306212 0.021938235 0.013306212 0.0029690173
0.013306212 0.059634306 0.09832035 0.059634306 0.013306212
0.021938235 0.09832035 0.16210285 0.09832035 0.021938235
0.013306212 0.059634306 0.09832035 0.059634306 0.013306212
0.0029690173 0.013306212 0.021938235 0.013306212 0.0029690173
The question is how to bring this kernel into a view suitable for two-pass rendering (horizontally and vertically, real time rendering in OpenGL)
EDIT:
Kernel given by book: 0.227027 0.1945946 0.1216216 0.054054 0.016216
My full fragment_blur_shader.glsl:
#version 330
out vec4 fragColor;
in vec2 texCoords;
uniform sampler2D image;
uniform bool isHorizontal;
uniform float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
void main() {
vec2 texOffset = 1.0 / textureSize(image, 0); // gets size of single texel
vec3 result = texture(image, texCoords).rgb * weight[0]; // current fragment’s contribution
if(isHorizontal) {
for(int i = 1; i < 5; i++) {
result += texture(image, texCoords + vec2(texOffset.x * i, 0.0)).rgb * weight[i];
result += texture(image, texCoords - vec2(texOffset.x * i, 0.0)).rgb * weight[i];
}
} else {
for(int i = 1; i < 5; ++i) {
result += texture(image, texCoords + vec2(0.0, texOffset.y * i)).rgb * weight[i];
result += texture(image, texCoords - vec2(0.0, texOffset.y * i)).rgb * weight[i];
}
}
fragColor = vec4(result, 1.0);
}
Also I found the following picture demonstrating the receipt of a 2D kernel from 1D kernel for a two-stage rendering:
But I have no idea how to get this 1D core. I am hope for your help.
EDIT:
I understood how to get the kernel I needed, but I still do not understand why the book gives this kernel in this form
To go from the 2D Gaussian kernel that you have:
float[][] kernel = new float[size][size];
int mean = size / 2;
float sum = 0; // For accumulating the kernel values
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
kernel[x][y] = (float) (Math.exp(-0.5 * (Math.pow((x - mean) / sigma, 2.0) +
Math.pow((y - mean) / sigma, 2.0))) / (2 * Geometry.PI * sigma * sigma));
// Accumulate the kernel values
sum += kernel[x][y];
}
}
// Normalize the kernel
for (int x = 0; x < size; x++)
for (int y = 0; y < size; y++)
kernel[x][y] /= sum;
to a 1D Gaussian kernel, simply remove the loops over y
(and all further references to y
):
float[] kernel = new float[size];
int mean = size / 2;
float sum = 0; // For accumulating the kernel values
for (int x = 0; x < size; x++) {
kernel[x] = (float) Math.exp(-0.5 * Math.pow((x - mean) / sigma, 2.0));
// Accumulate the kernel values
sum += kernel[x];
}
// Normalize the kernel
for (int x = 0; x < size; x++)
kernel[x] /= sum;
Some more hints:
As you already noticed, your shader uses only half of this kernel (the part for x-mean >= 0
). Since it is symmetric, the other half is redundant.
No need to scale the kernel
values with 2 * Geometry.PI * sigma * sigma
, because you normalize the kernel later, this scaling is irrelevant.
Multiplying this kernel with its transposed yields the 2D kernel that the first bit of code produces (as shown in the figure that you included in the question).