在计算着色器中计算帧缓冲区的颜色直方图
Calculating color histogram of framebuffer inside compute shader
如标题所示,我正在将场景渲染到帧缓冲区上,并试图从计算着色器内的帧缓冲区中提取颜色直方图。我完全不熟悉使用计算着色器,缺少 tutorials/examples/keywords 让我不知所措。
特别是,我正在努力正确设置计算着色器的输入和输出图像。这是我拥有的:
computeShaderProgram = loadComputeShader("test.computeshader");
int bin_size = 1;
int num_bins = 256 / bin_size;
tex_w = 1024;
tex_h = 768;
GLuint renderFBO, renderTexture;
GLuint tex_output;
//defining output image that will contain the histogram
glGenTextures(1, &tex_output);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_output);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, num_bins, 3, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
glBindImageTexture(0, tex_output, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R16UI);
//defining the framebuffer the scene will be rendered on
glGenFramebuffers(1, &renderFBO);
glGenTextures(1, &renderTexture);
glBindTexture(GL_TEXTURE_2D, renderTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W_WIDTH, W_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindFramebuffer(GL_FRAMEBUFFER, renderFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTexture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
在主循环中,我在帧缓冲区上绘制了一个简单的正方形,并尝试将帧缓冲区作为输入图像传递给计算着色器:
glBindFramebuffer(GL_FRAMEBUFFER, renderFBO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glUseProgram(computeShaderProgram);
//use as input the drawn framebuffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, renderFBO);
//use as output a pre-defined texture image
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex_output);
//run compute shader
glDispatchCompute((GLuint)tex_w, (GLuint)tex_h, 1);
GLuint *outBuffer = new GLuint[num_bins * 3];
glGetTexImage(GL_TEXTURE_2D, 0, GL_R16, GL_UNSIGNED_INT, outBuffer);
最后,在我的计算着色器中:
#version 450
layout(local_size_x = 1, local_size_y = 1) in;
layout(rgba32f, binding = 0) uniform readonly image2D img_input;
layout(r16ui, binding = 1) uniform writeonly image2D img_output;
void main() {
// grabbing pixel value from input image
vec4 pixel_color = imageLoad(img_input, ivec2(gl_GlobalInvocationID.xy));
vec3 rgb = round(pixel_color.rgb * 255);
ivec2 r = ivec2(rgb.r, 0);
ivec2 g = ivec2(rgb.g, 1);
ivec2 b = ivec2(rgb.b, 2);
imageAtomicAdd(img_output, r, 1);
imageAtomicAdd(img_output, g, 1);
imageAtomicAdd(img_output, b, 1);
}
我将输出定义为大小为 N x 3
的二维纹理图像,其中 N
是 bin 的数量,3
表示各个颜色分量。在着色器中,我从输入图像中获取一个像素值,将其缩放到 0-255
范围内并增加直方图中的适当位置。
我无法验证这是否按预期工作,因为计算着色器会产生编译错误,即:
- 无法将布局 (r16ui) 应用于图像类型
"image2D"
- 无法找到兼容的重载函数
"imageAtomicAdd(struct image2D1x16_bindless, ivec2, int)"
- 编辑:更改为
r32ui
后,先前的错误现在变为:合格的实际参数 #1 无法转换为不太合格的参数(“im”)
如何正确配置我的计算着色器?
我的过程是否正确(至少在理论上),如果不正确,为什么?
关于您的问题:
can't apply layout(r16ui) to image type "image2D"
r16ui
只能应用于无符号图像类型,因此您应该使用uimage2D
.
unable to find compatible overloaded function ...
规范明确指出原子操作只能应用于 32 位类型(r32i
、r32ui
或 r32f
)。因此,您必须改用 32 位纹理。
您的代码中还有其他问题。
glBindTexture(GL_TEXTURE_2D, renderFBO);
您不能将 FBO 绑定到纹理。您应该改为绑定支持 FBO 的纹理 (renderTexture
)。
此外,您打算将纹理绑定到图像统一而不是采样器,因此您必须使用 glBindImageTexture
或 glBindImageTextures
而不是 glBindTexture
。使用后者,您可以在一个调用中绑定两个图像:
GLuint images[] = { renderTexture, tex_output };
glBindImageTextures(0, 2, images);
你的 img_output
制服标记为 writeonly
。然而,原子图像函数需要一个不合格的制服。所以删除 writeonly
.
您可以在 OpenGL 和 GLSL 规范中找到上述所有信息,可从 the OpenGL registry 免费访问。
如标题所示,我正在将场景渲染到帧缓冲区上,并试图从计算着色器内的帧缓冲区中提取颜色直方图。我完全不熟悉使用计算着色器,缺少 tutorials/examples/keywords 让我不知所措。
特别是,我正在努力正确设置计算着色器的输入和输出图像。这是我拥有的:
computeShaderProgram = loadComputeShader("test.computeshader");
int bin_size = 1;
int num_bins = 256 / bin_size;
tex_w = 1024;
tex_h = 768;
GLuint renderFBO, renderTexture;
GLuint tex_output;
//defining output image that will contain the histogram
glGenTextures(1, &tex_output);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_output);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, num_bins, 3, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
glBindImageTexture(0, tex_output, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R16UI);
//defining the framebuffer the scene will be rendered on
glGenFramebuffers(1, &renderFBO);
glGenTextures(1, &renderTexture);
glBindTexture(GL_TEXTURE_2D, renderTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W_WIDTH, W_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindFramebuffer(GL_FRAMEBUFFER, renderFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTexture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
在主循环中,我在帧缓冲区上绘制了一个简单的正方形,并尝试将帧缓冲区作为输入图像传递给计算着色器:
glBindFramebuffer(GL_FRAMEBUFFER, renderFBO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glUseProgram(computeShaderProgram);
//use as input the drawn framebuffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, renderFBO);
//use as output a pre-defined texture image
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex_output);
//run compute shader
glDispatchCompute((GLuint)tex_w, (GLuint)tex_h, 1);
GLuint *outBuffer = new GLuint[num_bins * 3];
glGetTexImage(GL_TEXTURE_2D, 0, GL_R16, GL_UNSIGNED_INT, outBuffer);
最后,在我的计算着色器中:
#version 450
layout(local_size_x = 1, local_size_y = 1) in;
layout(rgba32f, binding = 0) uniform readonly image2D img_input;
layout(r16ui, binding = 1) uniform writeonly image2D img_output;
void main() {
// grabbing pixel value from input image
vec4 pixel_color = imageLoad(img_input, ivec2(gl_GlobalInvocationID.xy));
vec3 rgb = round(pixel_color.rgb * 255);
ivec2 r = ivec2(rgb.r, 0);
ivec2 g = ivec2(rgb.g, 1);
ivec2 b = ivec2(rgb.b, 2);
imageAtomicAdd(img_output, r, 1);
imageAtomicAdd(img_output, g, 1);
imageAtomicAdd(img_output, b, 1);
}
我将输出定义为大小为 N x 3
的二维纹理图像,其中 N
是 bin 的数量,3
表示各个颜色分量。在着色器中,我从输入图像中获取一个像素值,将其缩放到 0-255
范围内并增加直方图中的适当位置。
我无法验证这是否按预期工作,因为计算着色器会产生编译错误,即:
- 无法将布局 (r16ui) 应用于图像类型
"image2D"
- 无法找到兼容的重载函数
"imageAtomicAdd(struct image2D1x16_bindless, ivec2, int)"
- 编辑:更改为
r32ui
后,先前的错误现在变为:合格的实际参数 #1 无法转换为不太合格的参数(“im”)
如何正确配置我的计算着色器? 我的过程是否正确(至少在理论上),如果不正确,为什么?
关于您的问题:
can't apply layout(r16ui) to image type "image2D"
r16ui
只能应用于无符号图像类型,因此您应该使用uimage2D
.
unable to find compatible overloaded function ...
规范明确指出原子操作只能应用于 32 位类型(r32i
、r32ui
或 r32f
)。因此,您必须改用 32 位纹理。
您的代码中还有其他问题。
glBindTexture(GL_TEXTURE_2D, renderFBO);
您不能将 FBO 绑定到纹理。您应该改为绑定支持 FBO 的纹理 (renderTexture
)。
此外,您打算将纹理绑定到图像统一而不是采样器,因此您必须使用 glBindImageTexture
或 glBindImageTextures
而不是 glBindTexture
。使用后者,您可以在一个调用中绑定两个图像:
GLuint images[] = { renderTexture, tex_output };
glBindImageTextures(0, 2, images);
你的 img_output
制服标记为 writeonly
。然而,原子图像函数需要一个不合格的制服。所以删除 writeonly
.
您可以在 OpenGL 和 GLSL 规范中找到上述所有信息,可从 the OpenGL registry 免费访问。