【问题标题】:Why does QGLWidget only render a blank screen?为什么 QGLWidget 只渲染一个空白屏幕?
【发布时间】:2015-03-16 08:10:42
【问题描述】:

我之前在 SDL 中使用过 OpenGL,但是我刚刚开始学习 QT。在 QT 中,使用 OpenGL 被证明有点痛苦。我有以下两个文件:

main.cpp

#include <stdio.h>
#include <QApplication>
#include "glwidget.hpp"
#include <QGLFormat>

int main(int args, char *argv[]) {
    QApplication app(args, argv);

    GLWidget openGLWidget;
    openGLWidget.show();

    return app.exec();
}

glwidget.hpp

#include <GL/glew.h>
#include <QGLWidget>
class GLWidget : public QGLWidget {
    protected:
        void initializeGL();
        void paintGL();
};

void GLWidget::initializeGL() {
    if(glewInit() != GLEW_OK) {
        fprintf(stderr, "GLEW failed to initialize.");
    }   
}
void GLWidget::paintGL() {
    glClearColor(1, 0, 0, 1); 
    glClear(GL_COLOR_BUFFER_BIT);
    QGLWidget::swapBuffers();
}

helloworld.pro

TEMPLATE = app 
INCLUDEPATH += .
LIBS += -lGL -lGLEW

# Input
SOURCES += main.cpp
HEADERS += glwidget.hpp
QT += widgets opengl

当我编译并运行它时,我得到一个窗口,上面印有创建时的任何内容。我期待的是一个红屏。我错过了什么?

更新

我已经编辑了我的 GLWidget 实现,我让它工作了。但是,它仅在我调用 glDrawArrays 时才有效(请参阅下面的 paintGL 函数)。在 SDL 中,不需要 glDrawArrays 来查看空白的彩色屏幕。如果没有 glDrawArrays,qt 似乎出于某种原因忽略了 glClear()。有谁知道为什么?

GLWidget::GLWidget(QGLWidget* parent) : QGLWidget(QGLFormat(), parent) {
}

void GLWidget::initializeGL() {
    if(glewInit() != GLEW_OK) {
        fprintf(stderr, "GLEW failed to initialize.");
    }   
    glClearColor(1, 0, 0, 1); 
    glClear(GL_COLOR_BUFFER_BIT);
    shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, 
                                          "vertexShader.vert");
    shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, 
                                          "fragmentShader.frag");
    shaderProgram.link();
    GLuint vertexBuffer; 
    glGenBuffers(1, &vertexBuffer);
}

void GLWidget::paintGL() {
    shaderProgram.bind();
    glClearColor(1, 0, 0, 1); 
    glClear(GL_COLOR_BUFFER_BIT);
    setAutoBufferSwap(false);
    //glDrawArrays(GL_TRIANGLES, 0, 1);
    swapBuffers();
    shaderProgram.release();
}

void GLWidget::resizeGL(int width, int height) {
    if(height == 0) {
        height = 1;
    }   

    if(width == 0) {
        width = 1;
    }   

    glViewport(0, 0, width, height);
}

更新 2

我认为 Qt 可能在幕后偷偷摸摸地做了一些事情,如果我手动完成所有事情,我就会摆脱这个问题。但是 qt 仍然以某种方式知道我是否使用程序,以及我是否使用 glDrawArrays。在下面的代码中,取出 glDrawArrays 或 glUseProgram 会使代码不起作用。它必须与 QGLContext 内部发生的事情有关。

#include <stdio.h>
#include <fstream>
#include <string>
#include "glwidget.hpp"

GLWidget::GLWidget(QWidget* parent) : QGLWidget(QGLFormat(), parent) {
}

void GLWidget::initializeGL() {
    if(glewInit() != GLEW_OK) {
        fprintf(stderr, "GLEW failed to initialize.");
    }
    glContext = this->context();
    if(!glContext->create()) {
        fprintf(stderr, "Failed to create context.\n");
    }
    glContext->makeCurrent();
    program = glCreateProgram();
    addShader(program, GL_VERTEX_SHADER, "vertexShader.vert");
    addShader(program, GL_FRAGMENT_SHADER, "fragmentShader.frag");
    linkProgram(program);
    setAutoBufferSwap(false);
}

void GLWidget::paintGL() {
    glUseProgram(program);
    glClearColor(1, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    glDrawArrays(GL_TRIANGLES, 0, 1);
    glContext->swapBuffers();
    glUseProgram(0);
}

void GLWidget::resizeGL(int width, int height) {
    if(height == 0) {
        height = 1;
    }

    if(width == 0) {
        width = 1;
    }

    glViewport(0, 0, width, height);
}  

GLuint GLWidget::addShader(GLuint programID, GLuint shaderType, std::string fileName) {
    GLuint shader = glCreateShader(shaderType);
    std::ifstream file(fileName.c_str());
    std::string source = "";

    if(file.is_open()) {
        std::string line;
        while(getline(file, line)) {
            source += line + "\n";
        }
    } else {
        fprintf(stderr, "File %s failed to open.\n", fileName.c_str());
    }
    const char* sourceC = source.c_str();
    glShaderSource(shader, 1, &sourceC, NULL);

    glCompileShader(shader);
    GLint compileStatus;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
    if(compileStatus == GL_FALSE) {
        fprintf(stderr, "Shader %s failed to compile.\n", fileName.c_str());
        return 0;
    }
    glAttachShader(programID, shader);
    return shader;
}

void GLWidget::linkProgram(GLuint programID) {
    glLinkProgram(programID);
    GLint linkStatus;
    glGetProgramiv(programID, GL_LINK_STATUS, &linkStatus);
    if(linkStatus == GL_FALSE) {
        fprintf(stderr,"Failed to link program.\n");
    }
}

【问题讨论】:

  • 原因可能是这样的:QT docs for void QGLWidget::swapBuffers(): >用屏幕外缓冲区交换屏幕内容。这仅在小部件的格式指定双缓冲模式时才有效。 >通常情况下,不需要显式调用此函数,因为它会在每个小部件重绘后自动完成,即每次执行 paintGL() 后。
  • @rpress 我曾尝试过这个想法,但我认为这不是问题所在。查看更新了解更多详情。
  • @WilliamOliver 这是最初的问题 - 至少,我可以在您的原始代码中重现“空白屏幕”,并通过删除调用来制作所需的红色屏幕eitherswapBuffers 或添加对setAutoBufferSwap(false) 的调用。这没有用吗?
  • @Lack 不幸的是,没有。由于某种原因它没有工作。你说的很有道理。但对我来说,除非我调用 glDrawArrays,否则它根本不起作用。即使在原始示例中也是如此。
  • 我在 linux 上的事实会影响什么吗?

标签: c++ qt opengl ubuntu qglwidget


【解决方案1】:

您的 PC 可能存在配置问题。

这是我在 debian amd64/stable 和 Qt4.8 上使用的示例。

标题

#ifndef GLWIDGET_HPP
#define GLWIDGET_HPP
#include <QGLWidget>

class GlWidget:
public QGLWidget
{
    public:
        GlWidget(QWidget *parent=0);
        ~GlWidget();
    protected:
        void initializeGL();
        void paintGL();
};

#endif // GLWIDGET_HPP

实现

#include "glwidget.hpp"

    GlWidget::GlWidget(QWidget* parent)
        : QGLWidget(QGLFormat(), parent)
    {
    }
    GlWidget::~GlWidget()
    {

    }

    void GlWidget::initializeGL()
     {
     }

    void GlWidget::paintGL() 
    {
        glClearColor(1.f, 0.f, 0.f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT);

    }

我看到的唯一问题是创建 GLEW 拥有的另一个窗口。但是你可以关闭它。 QGLWidget 实例在右下角提升。

【讨论】:

    【解决方案2】:

    我找到了解决问题的方法。 QGLWidget 已弃用。以后看到这个问题的任何人都应该改用 QOpenGLWidget 。

    #include "GLShaderWidget.hpp"
    GLShaderWidget::GLShaderWidget(QWidget* parent) : QOpenGLWidget(parent)
    {
    }
    
    GLShaderWidget::~GLShaderWidget() {
    }
    
    
    void GLShaderWidget::initializeGL() {
            glClearColor(1, 0, 0, 1);
    
    }
    
    void GLShaderWidget::paintGL() {
            glClear(GL_COLOR_BUFFER_BIT);
    }
    

    这段代码运行良好。唯一的区别是我使用的是 QOpenGLWidget 而不是 QGLWidget。无论如何它都比 QGLWidget 好得多,因为它会自动调整视口的大小,并且 实际上 在内部使用了两个帧缓冲区(显然 QGLWidget 只是假装使用两个缓冲区)。

    【讨论】:

    • 你没有提到你使用的是Qt5。
    • @FabienR 我不是,但文档说要避免使用 4.8
    • 我会说QOpenGLWidget出现在Qt5.4中。
    猜你喜欢
    • 2020-02-17
    • 1970-01-01
    • 2022-11-28
    • 1970-01-01
    • 2019-12-24
    • 2014-03-18
    • 2017-02-12
    • 2020-02-07
    • 2018-12-10
    相关资源
    最近更新 更多