在计算着色器中计算帧缓冲区的颜色直方图

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 范围内并增加直方图中的适当位置。

我无法验证这是否按预期工作,因为计算着色器会产生编译错误,即:

如何正确配置我的计算着色器? 我的过程是否正确(至少在理论上),如果不正确,为什么?

关于您的问题:

can't apply layout(r16ui) to image type "image2D"

r16ui只能应用于无符号图像类型,因此您应该使用uimage2D.

unable to find compatible overloaded function ...

规范明确指出原子操作只能应用于 32 位类型(r32ir32uir32f)。因此,您必须改用 32 位纹理。


您的代码中还有其他问题。

glBindTexture(GL_TEXTURE_2D, renderFBO);

您不能将 FBO 绑定到纹理。您应该改为绑定支持 FBO 的纹理 (renderTexture)。

此外,您打算将纹理绑定到图像统一而不是采样器,因此您必须使用 glBindImageTextureglBindImageTextures 而不是 glBindTexture。使用后者,您可以在一个调用中绑定两个图像:

GLuint images[] = { renderTexture, tex_output };
glBindImageTextures(0, 2, images);

你的 img_output 制服标记为 writeonly。然而,原子图像函数需要一个不合格的制服。所以删除 writeonly.


您可以在 OpenGL 和 GLSL 规范中找到上述所有信息,可从 the OpenGL registry 免费访问。