Mac OS X 上的 OpenGL 纹理损坏

Posted

技术标签:

【中文标题】Mac OS X 上的 OpenGL 纹理损坏【英文标题】:OpenGL texture corruption on Mac OS X 【发布时间】:2015-09-09 17:12:46 【问题描述】:

我有一个程序可以在许多不同机器上的 Windows 和 Linux 上运行良好。它在我尝试过的任何 Mac 上都无法正常工作(很少有不同的机器使用 OS X 10.9 和 10.10)。该程序基本上只是在屏幕上渲染一个带纹理的四边形。纹理格式为浮点数。

在 Windows 和 Linux 上看起来像这样:

这是它在 Mac 上的样子:

所以纹理数据到达 GPU 后却被奇怪地扭曲了。纹理环绕模式设置为钳位,您可以从图片中看到它的行为方式。

这是最小的绘图代码:

void Framebuffer::initialize()

    Settings& settings = App::getSettings();

    glGenTextures(1, &imageTextureId);
    glBindTexture(GL_TEXTURE_2D, imageTextureId);
    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_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    resampleProgramId = GLHelper::buildProgram(settings.framebuffer.resampleVertexShader, settings.framebuffer.resampleFragmentShader);

    resampleTextureUniformId = glGetUniformLocation(resampleProgramId, "texture0");
    resampleTextureWidthUniformId = glGetUniformLocation(resampleProgramId, "textureWidth");
    resampleTextureHeightUniformId = glGetUniformLocation(resampleProgramId, "textureHeight");
    resampleTexelWidthUniformId = glGetUniformLocation(resampleProgramId, "texelWidth");
    resampleTexelHeightUniformId = glGetUniformLocation(resampleProgramId, "texelHeight");

    const GLfloat vertexData[] = 
        -1.0f, -1.0f, 0.0f, 0.0f,
        1.0f, -1.0f, 1.0f, 0.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        -1.0f, -1.0f, 0.0f, 0.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        -1.0f, 1.0f, 0.0f, 1.0f,
    ;

    glGenBuffers(1, &vertexBufferId);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);

    glGenVertexArrays(1, &vaoId);
    glBindVertexArray(vaoId);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
    glBindVertexArray(0);

    resize(settings.window.width, settings.window.height);


void Framebuffer::resize(int width, int height)

    image.resize(width, height);
    floatPixelData.resize(width * height * sizeof(float) * 4);

    // reserve the texture memory on the device
    glBindTexture(GL_TEXTURE_2D, imageTextureId);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, (GLsizei)width, (GLsizei)height, 0, GL_RGBA, GL_FLOAT, 0);
    glGenerateMipmap(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);


void Framebuffer::render()

    int imageWidth = image.getWidth();
    int imageHeight = image.getHeight();

    glUseProgram(resampleProgramId);
    glUniform1i(resampleTextureUniformId, 0);

    std::vector<Color>& imagePixelData = image.getPixelData();

    // convert image data from Color to float array
    for (int i = 0; i < (int)imagePixelData.size(); ++i)
    
        int pixelIndex = i * 4;

        floatPixelData[pixelIndex] = (float)imagePixelData[i].r;
        floatPixelData[pixelIndex + 1] = (float)imagePixelData[i].g;
        floatPixelData[pixelIndex + 2] = (float)imagePixelData[i].b;
        floatPixelData[pixelIndex + 3] = (float)imagePixelData[i].a;
    

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, imageTextureId);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)imageWidth, (GLsizei)imageHeight, GL_RGBA, GL_FLOAT, &floatPixelData[0]);
    glGenerateMipmap(GL_TEXTURE_2D);

    glBindVertexArray(vaoId);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    glBindVertexArray(0);

    glBindTexture(GL_TEXTURE_2D, 0);
    glUseProgram(0);

    GLHelper::checkError("Could not render the framebuffer");

为了测试,所有着色器都被替换为最小的颜色传递版本。

这是我没有运气的尝试:

不同的内部纹理格式 调用glGenerateMipmap的不同组合 不同的顶点和纹理坐标值 不同的纹理钳位和最小/最大过滤器设置(mipmap 开/关) 不同的视口值

相同的代码在 Windows 和 Linux 上运行良好。

只有 Mac 可能导致这种损坏?

【问题讨论】:

由于实际的纹理存储是通过glTexImage() 创建的,因此不清楚您的代码是否创建了有效的纹理。 framebufferTextureID 的存储是在setWindowSize() 中创建的,它在Initialize() 期间被调用,但只有在保证在您尝试渲染之前调用Frambuffer::resize() 时才会创建存储imageTextureID,这可能只是不会就是这样。另请注意,您使用浮点纹理。您使用内部格式GL_RGBA,这是一种标准化整数格式,每通道 8 位。 文本是在纹理的左上角部分,还是在绘制纹理四边形后将其渲染在顶部? @derhass resize 保证会被调用。内部格式的东西很好,我会尝试改变它。 @reto 是的,文本被渲染为另一个带有 freetype-gl 的纹理四边形。 -- 我只是想知道这种渲染错误是否是一些已知问题。 【参考方案1】:

我不确定这是否是您代码中的唯一问题(如果问题完全出在您的代码中),但您的 mipmap 生成肯定存在问题。你有这个调用序列:

glBindTexture(GL_TEXTURE_2D, framebufferTextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)windowWidth, (GLsizei)windowHeight, 0, GL_RGBA, GL_FLOAT, 0);
glGenerateMipmap(GL_TEXTURE_2D);

glGenerateMipmap() 不是一个状态设置调用,它指定您希望在纹理填充数据后生成 mipmap。它的作用是在调用时从纹理中的数据生成 mipmap。由于您刚刚在上一次调用中分配了纹理数据,而纹理中还没有数据,glGenerateMipmap() 将什么也不做。

稍后,您将使用此纹理:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, framebufferTextureId);

此时,纹理没有 mipmap,即使采样模式设置为使用 mipmap。

您需要做的是在纹理填充数据后生成 mipmap。您可以从setWindowSize() 中的当前位置删除glGenerateMipmap() 调用,例如将其移动到上面第二个编码器片段的下方:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, framebufferTextureId);

glGenerateMipmap(GL_TEXTURE_2D);

【讨论】:

在测试了这个和许多其他东西之后,我已经编辑了我的问题。不幸的是,还没有解决它。【参考方案2】:

好吧,我终于想通了。顶点着色器内的属性位置在 Mac OS X 上被切换。幸运的是,它在屏幕上渲染了一些东西,使用 texcoords 作为顶点位置,反之亦然。

我本可以使用glBindAttribLocation 来修复它,但我将着色器更新到版本 330 并使用了layout(location = 0) 属性。这也很好用。

【讨论】:

以上是关于Mac OS X 上的 OpenGL 纹理损坏的主要内容,如果未能解决你的问题,请参考以下文章

Mac OS X Mojave 上的 XAMPP 错误

Mac OS X安装OpenGL

Mac OS X 更新后损坏的 python

在 Mac OS X 上使用现代 OpenGL [关闭]

在 Mac OS X 上使用 CGL 设置 OpenGL 上下文

在 Mac OS X 中编译使用 OpenGL 的 C 程序