Why does QGLWidget only render a blank screen?
Asked Answered
R

2

15

I have used OpenGL in SDL before, but I have just started to learn QT. In QT, using OpenGL is proving to be somewhat of a pain. I have the following two files:

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

When I compile and run this, I get a window that has whatever was behind it at the time of creation imprinted on it. What I am expecting is a red screen. What am I missing?

UPDATE

I have edited my GLWidget implementation, and I got it to work. However, it only works when I call glDrawArrays (see the paintGL function below). In SDL, glDrawArrays wasn't necessary to see a blank colored screen. Without glDrawArrays, qt seems to ignore glClear() for some reason. Does anyone know why?

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);
}

UPDATE 2

I thought that maybe Qt was doing something sneaky under the hood, and that if I did everything manually, I would get rid of the problem. But qt still somehow knows whether I am using a program or not, and whether I am using glDrawArrays or not. In the code below, taking out either glDrawArrays or glUseProgram makes the code not work. It must have something to do with what happens inside 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");
    }
}
Rationality answered 17/1, 2015 at 23:56 Comment(7)
The reason may be this: QT docs for void QGLWidget::swapBuffers(): >Swaps the screen contents with an off-screen buffer. This only works if the widget's format specifies double buffer mode. >Normally, there is no need to explicitly call this function because it is done automatically after each widget repaint, i.e. each time after paintGL() has been executed.Spiller
@Spiller I played with that idea a little bit, but I don't think that is the problem. See the update for more details.Rationality
@WilliamOliver that was the original problem - at least, I can reproduce the "blank screen" in your original code, and make the desired red screen either by removing the call to swapBuffers or by adding a call to setAutoBufferSwap(false). Did that not work?Superhuman
@Superhuman unfortunately, no. It didn't work for some reason. What you said would make sense. But for me, it doesn't work at all unless I have a call to glDrawArrays. Even in the original example.Rationality
Should the fact that I am on linux affect anything?Rationality
@Superhuman actually, in the original example, it doesn't work regardless of whether I have glDrawArrays or not. It needs both a shaderProgram and a call to glDrawArrays. It doesn't even require that I have anything sent to my shader programs, it only requires that the size parameter of glDrawArrays is non-zero.Rationality
@WilliamOliver I don't know, sorry.Superhuman
R
6

I found the solution to my problem. QGLWidget is deprecated. Anyone who sees this question in the future should use QOpenGLWidget instead.

#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);
}

This code works just fine. The only difference is that I am using QOpenGLWidget instead of QGLWidget. It is far better than QGLWidget anyways, because it automatically re-sizes the view port, and actually uses two frame-buffers internally (apparently QGLWidget was just pretending to use two buffers).

Rationality answered 8/2, 2015 at 4:35 Comment(3)
You did not mention that you were using Qt5.Scrouge
@FabienR I wasn't, but the documentation says to avoid using 4.8Rationality
I would say that QOpenGLWidget appeared in Qt5.4.Scrouge
S
0

You may have a configuration problem on your PC.

Here is the example I use on debian amd64/stable with Qt4.8.

header

#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

implementation

#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);

    }

The only problem I see is the creation of another window owned by GLEW. But you may close it. The QGLWidget instance is promoted at the bottom right.

QGLWidget

Scrouge answered 7/2, 2015 at 17:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.