OpenGL计算着色器 - 奇怪的结果

时间:2016-10-28 15:32:48

标签: c++ opencv opengl image-processing compute-shader

我正在尝试为图像处理实现多通道计算着色器。 每次通过都有输入图像和输出图像。 下一个'输入图像是前一个'输出。

这是我第一次在OpenGL中使用计算着色器,因此我的设置可能存在一些问题。 我正在使用OpenCV的Mat作为读取/复制操作的容器。

代码的某些部分与问题无关,所以我没有包含。其中一些部分包括加载图像或初始化上下文。

初始化:

//texture init
glGenTextures(1, &feedbackTexture_);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, feedbackTexture_);
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);
glBindTexture(GL_TEXTURE_2D, 0);

glGenTextures(1, &resultTexture_);
glActiveTexture(GL_TEXTURE0+1);
glBindTexture(GL_TEXTURE_2D, resultTexture_);
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);
glBindTexture(GL_TEXTURE_2D, 0);

// shader init
computeShaderID = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(computeShaderID, 1, &computeShaderSourcePtr, &computeShaderLength);
glCompileShader(computeShaderID);
programID = glCreateProgram();
glAttachShader(programID, computeShaderID);
glLinkProgram(programID);
glDeleteShader(computeShaderID);

着色器代码:

//shader code (simple invert)
#version 430
layout (local_size_x = 1, local_size_y = 1) in;

layout (location = 0, binding = 0, /*format*/ rgba32f) uniform readonly image2D inImage;
layout (location = 1, binding = 1, /*format*/ rgba32f) uniform writeonly image2D resultImage;

uniform writeonly image2D image;

void main()
{
    // Acquire the coordinates to the texel we are to process.
    ivec2 texelCoords = ivec2(gl_GlobalInvocationID.xy);

    // Read the pixel from the first texture.
    vec4 pixel = imageLoad(inImage, texelCoords);

    pixel.rgb = 1. - pixel.rgb;

    imageStore(resultImage, texelCoords, pixel);
}

用法:

cv::Mat image = loadImage().clone();
cv::Mat result(image.rows,image.cols,image.type());
// These get the appropriate enums used by glTexImage2D
GLenum internalformat = GLUtils::getMatOpenGLImageFormat(image);
GLenum format = GLUtils::getMatOpenGLFormat(image);
GLenum type = GLUtils::getMatOpenGLType(image);

int dispatchX = 1;
int dispatchY = 1;

for ( int i = 0; i < shaderPasses_.size(); ++i)
{
    // Update textures
    glBindTexture(GL_TEXTURE_2D, feedbackTexture_);
    glTexImage2D(GL_TEXTURE_2D, 0, internalformat, result.cols, result.rows, 0, format, type, result.data);
    glBindTexture(GL_TEXTURE_2D, resultTexture_);
    glTexImage2D(GL_TEXTURE_2D, 0, internalformat, image.cols, image.rows, 0, format, type, 0);
    glBindTexture(GL_TEXTURE_2D, 0);

    glClear(GL_COLOR_BUFFER_BIT);
    std::shared_ptr<Shader> shaderPtr = shaderPasses_[i];
    // Enable shader
    shaderPtr->enable();
    {
        // Bind textures
        // location = 0, binding = 0
        glUniform1i(0,0);
        // binding = 0
        glBindImageTexture(0, feedbackTexture_, 0, GL_FALSE, 0, GL_READ_ONLY, internalformat);
        // location = 1, binding = 1
        glUniform1i(1,1);
        // binding = 1
        glBindImageTexture(1, resultTexture_, 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat);

        // Dispatch rendering
        glDispatchCompute((GLuint)image.cols/dispatchX,(GLuint)image.rows/dispatchY,1);
        // Barrier will synchronize
        glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
    }
    // disable shader
    shaderPtr->disable();

    // Here result is now the result of the last pass.
}

有时我得到奇怪的结果(毛刺纹理,部分渲染纹理),第一个像素(0,0)有时也不会被写入。 我是否正确设置了一切,还是我缺少了什么? 看来这种带纹理的方法真的很慢,有没有其他可以提高性能的方法呢?

编辑1: 更改了记忆障碍标记。

3 个答案:

答案 0 :(得分:5)

glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

这是一个错误的障碍。屏障指定how you intend to access the data after the incoherent accesses。如果您尝试使用glGetTexImage读取纹理,则必须使用GL_TEXTURE_UPDATE_BARRIER_BIT

答案 1 :(得分:3)

我不能100%确定这是否能解决您的问题;但是我没有看到你的标志显然有任何错误,无法初始化你的纹理设置。当我将您的代码与我的项目进行比较时,API调用的顺序引起了我的注意。在您的来源中,您有此订单:

glGenTextures(...);    // Generate
glActiveTexture(...);  // Set Active
glBindTexture(...);    // Bind Texture
glTexParameteri(...);  // Wrap Setting
glTexParameteri(...);  // Wrap Setting
glTexParameteri(...);  // Mipmap Setting
glTexParameteri(...);  // Mipmap Setting
glBindTexture(...);    // Bind / Unbind

并为每个纹理重复此操作,除了传递纹理变量和增加id值。

我不知道它是否有所作为,但是在我的引擎中并遵循我设置的逻辑路径;尝试按此顺序执行,看看是否有任何区别

glGenTextures(...);    // Generate
glBindTexture(...);    // Bind Texture
glTexParameteri(...);  // Wrap Setting
glTexParameteri(...);  // Wrap Setting
glTexParameteri(...);  // Mipmap Setting
glTexParameteri(...);  // Mipmap Setting

glActiveTexture(...);  // Set Active
glBindTexture(...);    // Bind / Unbind

我没有使用计算着色器,但在我的引擎中,我有几个管理不同内容的类。我有一个资产存储,它将所有资产保存到包含图像纹理的内存数据库中,我有一个ShaderManager类来管理目前只使用顶点和片段着色器的不同着色器。它将读入并编译着色器文件,创建着色器程序,设置属性和制服,链接程序并运行着色器。我正在使用批处理过程,我有一个批处理类和一个批处理管理器类来渲染不同类型的基元。因此,当我通过我的解决方案并遵循逻辑路径或流程时,这就是我在代码中看到的内容。

正是AssetStorage类正在设置纹理的属性,它在add()函数中按此顺序调用这些API调用,以便将纹理添加到内存中。

 glGenTextures(...);
 glBindTextures(...);
 glTexParameteri(...);
 glTexParameteri(...);
 glTexParameteri(...);
 glTexParameteri(...);

然后AssetStorage也在调用这些

glPixelStorei(...);
glTexImage2D(...)

将纹理添加到AssetStorage中的函数最终将返回TextureInfo对象的自定义结构。

当我在其render()函数调用下检查我的批次类时,这是调用ShaderManager的函数来设置制服使用纹理,然后调用ShaderManager的函数来设置如果纹理包含alpha通道,则再次设置纹理。在setTexture()函数的ShaderManger类中,这是最终调用glActiveTexture()glBindTexture()的位置。

因此,在简短摘要中,请尝试将glActiveTexture()调用移至两个纹理的最后glTexParameter()和最后glBindTexture()次调用之间。我也认为它也应该在这两个电话之后glPixelStorei()&amp; glTexImage2D()因为您想要将纹理设置为活动状态,就像您要渲染它一样。

正如我之前提到的那样,我不能100%确定这是否是您问题的根本原因,但我相信值得尝试一下,看看它是否对您有所帮助。如果你试试这个,请告诉我会发生什么。我想知道这些API调用的顺序是否对它有任何影响。我会在我自己的解决方案中尝试它,但我不想打破我的课程或项目,因为它目前正常工作。

作为一个注释,纹理设置标志的唯一内容是在wrap / repeat部分中。您可以尝试使用GL_REPEAT进行前两次glTexParameteri()来电,而不是使用GL_CLAMP_TO_EDGE让我知道您提出的问题,您不必担心mipmap最近两次glTexParameteri()次调用的设置,因为您似乎没有使用您正在使用的设置中的mipmap。

答案 2 :(得分:3)

我终于可以解决这个问题了!

问题在于cv :: Mat的构造函数。 以下行仅为cv :: Mat创建标题:

cv::Mat result(image.rows,image.cols,image.type());

DOES 分配数据但 NOT 初始化该数据,这就是为什么我有这些奇怪的结果。这是记忆中的垃圾。

使用任何分配 AND 的函数初始化此数据可以解决问题:

cv::Mat::zeros
cv::Mat::ones
cv::Mat::create
相关问题