用于两次渲染的模糊内核分离
Blur kernel separation for two pass rendering
我需要计算二次模糊的内核。假设有一个blur kernel 5x5,生成如下:
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;
}
当 sigma = 1 和 size = 5 时,我们有以下内核:
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
问题是如何将这个内核带入适合二次渲染的视图(水平和垂直,在 OpenGL 中实时渲染)
编辑:
书本给出的内核:0.227027 0.1945946 0.1216216 0.054054 0.016216
我满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);
}
我还找到了下图,演示了从 1D 内核接收 2D 内核以进行两阶段渲染:
但我不知道如何获得这个一维核心。希望得到您的帮助。
编辑:
我明白了如何得到我需要的内核,但我还是不明白为什么书上会以这种形式给出这个内核
所以,我得出了最终的答案。多亏了我找到的图片和你的建议,我终于写出了获得一维二次核的最终函数:
public static float[] getTwoPassKernelf(int size, float sigma) {
float[] kernel = new float[size];
float[][] fkernel = getKernelf(size, sigma);
for (int i = 0; i < size; i++) {
kernel[i] = (float) Math.sqrt(fkernel[i][i]);
}
return kernel;
}
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;
}
总之,着色器需要传输的不是所有内核,而是它的一部分,包括中心(在我的例子中)。因此,我在问题本身中引用的核心不是 5x5 而是 10x10。
例如:getTwoPassKernelf(10, 2f)
returns
0.008890575, 0.027384898, 0.065692954, 0.1227306, 0.17857197, 0.20234855, 0.17857197, 0.1227306, 0.065692954, 0.027384898
但在我的情况下,我必须只接受 0.20234855, 0.17857197, 0.1227306, 0.065692954, 0.027384898
- 这正是书中给出的内容
一张图片解释了获取内核进行两次渲染的本质
选中-内核元素的平方
我希望答案对某人有用
从您拥有的 2D 高斯内核出发:
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;
到一维高斯核,只需删除 y
上的循环(以及对 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;
更多提示:
正如您已经注意到的,您的着色器只使用了这个内核的一半(x-mean >= 0
的部分)。由于是对称的,所以另一半是多余的。
无需使用 2 * Geometry.PI * sigma * sigma
缩放 kernel
值,因为稍后对内核进行标准化,此缩放无关紧要。
将这个核与其转置相乘得到代码第一位产生的二维核(如问题中包含的图所示)。
我需要计算二次模糊的内核。假设有一个blur kernel 5x5,生成如下:
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;
}
当 sigma = 1 和 size = 5 时,我们有以下内核:
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
问题是如何将这个内核带入适合二次渲染的视图(水平和垂直,在 OpenGL 中实时渲染)
编辑:
书本给出的内核:0.227027 0.1945946 0.1216216 0.054054 0.016216
我满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);
}
我还找到了下图,演示了从 1D 内核接收 2D 内核以进行两阶段渲染:
但我不知道如何获得这个一维核心。希望得到您的帮助。
编辑:
我明白了如何得到我需要的内核,但我还是不明白为什么书上会以这种形式给出这个内核
所以,我得出了最终的答案。多亏了我找到的图片和你的建议,我终于写出了获得一维二次核的最终函数:
public static float[] getTwoPassKernelf(int size, float sigma) {
float[] kernel = new float[size];
float[][] fkernel = getKernelf(size, sigma);
for (int i = 0; i < size; i++) {
kernel[i] = (float) Math.sqrt(fkernel[i][i]);
}
return kernel;
}
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;
}
总之,着色器需要传输的不是所有内核,而是它的一部分,包括中心(在我的例子中)。因此,我在问题本身中引用的核心不是 5x5 而是 10x10。
例如:getTwoPassKernelf(10, 2f)
returns
0.008890575, 0.027384898, 0.065692954, 0.1227306, 0.17857197, 0.20234855, 0.17857197, 0.1227306, 0.065692954, 0.027384898
但在我的情况下,我必须只接受 0.20234855, 0.17857197, 0.1227306, 0.065692954, 0.027384898
- 这正是书中给出的内容
一张图片解释了获取内核进行两次渲染的本质
选中-内核元素的平方
我希望答案对某人有用
从您拥有的 2D 高斯内核出发:
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;
到一维高斯核,只需删除 y
上的循环(以及对 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;
更多提示:
正如您已经注意到的,您的着色器只使用了这个内核的一半(
x-mean >= 0
的部分)。由于是对称的,所以另一半是多余的。无需使用
2 * Geometry.PI * sigma * sigma
缩放kernel
值,因为稍后对内核进行标准化,此缩放无关紧要。将这个核与其转置相乘得到代码第一位产生的二维核(如问题中包含的图所示)。