我正在使用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);
}
答案 0 :(得分:2)
QOpenGLWidget是一个相当复杂的抽象,它有一些你可能没想到的副作用。引自Qt5 docs:
所有渲染都发生在OpenGL帧缓冲对象中。
makeCurrent()
确保它在上下文中绑定。在paintGL()
中的渲染代码中创建和绑定其他帧缓冲对象时,请记住这一点。永远不要使用ID 0重新绑定帧缓冲区。而是调用defaultFramebufferObject()
来获取应该绑定的ID。
现在,这本身并不是问题。但是,查看initializeGL()
方法的描述(我的重点):
无需调用
makeCurrent()
,因为调用此函数时已经完成了此操作。 请注意,此阶段尚未提供帧缓冲,因此请避免从此处发出绘制调用。将此类电话转接至paintGL()
。
现在,这本身仍然不是问题。但是:这意味着Qt将在initializeGL
和第一个paintGL
之间创建FBO。由于Qt创建纹理作为FBO的颜色缓冲区,这意味着它将重新使用当前活动的纹理单元并更改您在initializeGL
中建立的纹理绑定。
另一方面,如果你将glActiveTexture
设置为单位0之外的其他东西,Qt会搞砸该单位的绑定,但由于你只使用单位0,它不会对你有任何负面影响示例
答案 1 :(得分:1)
您需要在绘制之前将纹理绑定到纹理单元。与制服不同,纹理单元状态不是程序状态的一部分。在程序启动期间尝试设置纹理单元状态是不常见的,这需要为每个程序分配不同的纹理单元(不是不可能的,它不是通常的方式)。
在绘制调用之前将以下行添加到paintGL
:
glBindTexture(GL_TEXTURE_2D, texture);