如果当前处于活动状态,则 OpenGL 纹理不会渲染

Posted

技术标签:

【中文标题】如果当前处于活动状态,则 OpenGL 纹理不会渲染【英文标题】:OpenGL texture not rendering if it's currently active 【发布时间】:2016-12-28 12:31:50 【问题描述】:

我使用 QOpenGLWidget 来渲染带纹理的三角形,代码看起来不错,但三角形总是呈现黑色我有两天的问题,直到我不小心发现了标题所说的内容。

这是代码,纹理被加载到默认位置 GL_TEXTURE0 除非我最后调用 glActiveTexture(GL_TEXTURE1),否则代码将不起作用,GL_TEXTURE1 只是一个示例,它可以是任何其他纹理槽,除了纹理实际所在的槽。如果没有调用,对象将是黑色的。

    QImage ready;
    QImage image("C:/Users/Gamer/Desktop/New folder/ring.jpg");
    ready = image.convertToFormat(QImage::Format_RGBA8888);


    glGenTextures(1, &texture);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(glGetUniformLocation(program.programId(), "samp"), 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ready.width(), ready.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, ready.constBits());
    glGenerateMipmap(GL_TEXTURE_2D);
    glActiveTexture(GL_TEXTURE1)

我已经尝试了一些测试,创建多个纹理并一次显示它们,最后一个活动纹理总是黑色的,除非我激活其他一些未占用的插槽。

我不知道该怎么做,我是 OpenGL 和 Qt 的初学者,但这听起来不对。

编辑:

主要功能

#include "mainwindow.h"
#include <QApplication>
#include <QSurfaceFormat>

int main(int argc, char *argv[])

    QApplication a(argc, argv);

    QSurfaceFormat format;
    format.setVersion(3, 3);
    format.setProfile(QSurfaceFormat::CoreProfile);
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setSamples(4);
    format.setSwapInterval(0);
    QSurfaceFormat::setDefaultFormat(format);

    MainWindow w;
    w.show();

    return a.exec();

小部件代码

#include "openglwidget.h"
#include <QOpenGLShaderProgram>
#include <QImage>
#include <QDebug>

OpenGLWidget::OpenGLWidget(QWidget *parent) :
    QOpenGLWidget(parent)




OpenGLWidget::~OpenGLWidget()

    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    glDeleteTextures(1, &texture);



void OpenGLWidget::initializeGL()

    QOpenGLFunctions_3_3_Core::initializeOpenGLFunctions();

    GLfloat vertices[] = 
            0.0f, 0.75f, 0.0f,
            -0.75f, -0.75f, 0.0f,
            0.75f, -0.75f, 0.0f,
            0.5f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f
        ;

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    program.addShaderFromSourceFile(QOpenGLShader::Vertex, "C:/Users/Gamer/Desktop/New folder/vertex.vert");
    program.addShaderFromSourceFile(QOpenGLShader::Fragment, "C:/Users/Gamer/Desktop/New folder/fragment.frag");
    program.link();
    program.bind();

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)36);
    glEnableVertexAttribArray(1);

    QImage ready;
    QImage image("C:/Users/Gamer/Desktop/New folder/ring.jpg");
    ready = image.convertToFormat(QImage::Format_RGBA8888);


    glGenTextures(1, &texture);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(glGetUniformLocation(program.programId(), "samp"), 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ready.width(), ready.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, ready.constBits());
    glGenerateMipmap(GL_TEXTURE_2D);
//    glActiveTexture(GL_TEXTURE1);



void OpenGLWidget::paintGL()

    GLfloat yellow[] = 1.0, 1.0, 0.0, 0.0;

    glClearBufferfv(GL_COLOR, 0, yellow);

    glDrawArrays(GL_TRIANGLES, 0, 3);


void OpenGLWidget::resizeGL(int w, int h)

    glViewport(0, 0, w, h);

和着色器

#version 330 core

layout(location = 0) in vec3 pos;
layout(location = 1) in vec2 coord;

out vec2 tc;

void main(void)

    tc = coord;
    gl_Position = vec4(pos, 1.0);


#version 330 core

uniform sampler2D samp;

in vec2 tc;
out vec4 color;

void main(void)

    color = texture(samp, tc);

【问题讨论】:

只是为了确定:你首先是活动单元,十个你绑定 txture 到它。你如何渲染它,你是否向采样器制服提供正确的纹理单元编号(0、1 等)或什么? 我刚刚粘贴了更多代码以显示我做了你所说的一切,但唯一的区别是最后一次调用 glActiveTexture,如果我评论它三角形变黑一切都很好。另外我应该提到我尝试使用 QOpenGLTexture,并且发生了同样的事情,也就是说,如果不激活另一个纹理单元,它就无法工作。 嗯,看起来您的代码正在绑定其他一些纹理(或 0)。 这只是带纹理的三角形,没有其他纹理,中间也没有,这是一个简单的例子,代码很少。我尝试对 SFML 做同样的事情,它可以工作,这与 Qt 有关,但我不知道如何。 没有相关代码,没人能告诉你。请注意,Qt 有一些抽象可能会干扰这里。 【参考方案1】:

QOpenGLWidget 是一个相当复杂的抽象,它有一些你可能没有想到的副作用。引用自Qt5 docs:

所有渲染都发生在一个 OpenGL 帧缓冲区对象中。 makeCurrent() 确保它绑定在上下文中。在paintGL() 的渲染代码中创建和绑定其他帧缓冲区对象时请记住这一点。切勿重新绑定 ID 为 0 的帧缓冲区。而是调用defaultFramebufferObject() 来获取应绑定的 ID。

现在,这本身不是问题。但是,查看initializeGL() 方法的描述(我的重点):

无需调用makeCurrent(),因为在调用此函数时已完成。 但是请注意,帧缓冲区在此阶段尚不可用,因此请避免从此处发出绘图调用。将此类呼叫推迟到 paintGL()

现在,这本身仍然不是问题。但是:这意味着 Qt 将在 initializeGL 和第一个 paintGL 之间创建 FBO。由于 Qt 创建了一个纹理作为 FBO 的颜色缓冲区,这意味着它将重新使用当前活动的纹理单元,并更改您在 initializeGL 中建立的纹理绑定。

另一方面,如果您将glActiveTexture 设置为单元 0 以外的其他值,Qt 将破坏该单元的绑定,但由于您只使用单元 0,因此在您的示例中不会产生任何负面影响。

【讨论】:

这很有道理,但我有一些问题。你怎么知道 Qt 重用了活动纹理单元 另一个问题,在 Qt 将它的纹理与 FBO 关联之后,我可以再次使用该纹理单元作为我自己的纹理吗?正如其他答案所建议的那样,我尝试将纹理再次重新绑定到paintGL中的同一纹理单元,并且它可以工作。我的意思是,如果 Qt 使用该单元,那是否安全? 好吧,如果你真的想的话,你可以在源代码中查找它。 QOpenGLWidget 在初始化时使用 QGLFramebufferObject 本身 creates and binds a texture。它改变纹理绑定的事实并不意味着您不能使用该纹理单元。它不消耗任何纹理单元,它只使用纹理。【参考方案2】:

您需要在绘制之前将纹理绑定到纹理单元。与制服不同,纹理单元状态不是程序状态的一部分。在程序启动期间尝试设置纹理单元状态是不常见的,这需要为每个程序分配不同的纹理单元(这不是不可能的,这不是通常的做法)。

将以下行添加到paintGL,在draw call之前:

glBindTexture(GL_TEXTURE_2D, texture);

【讨论】:

以上是关于如果当前处于活动状态,则 OpenGL 纹理不会渲染的主要内容,如果未能解决你的问题,请参考以下文章

python:如果后台线程处于活动状态,则不会运行weakref.finalize

确保释放 OpenGL 纹理内存

OpenGL 包装器中的 Alpha/纹理问题

有没有办法检测浏览器窗口当前是否处于活动状态?

OpenGL 闪烁/损坏,窗口调整大小和 DWM 处于活动状态

Laravel 5.4 检查用户是不是处于活动状态,如果没有则回复消息