如果当前处于活动状态,则 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