2015-01-17 10 views
13

Ho già usato OpenGL in SDL, ma ho appena iniziato a imparare QT. In QT, l'uso di OpenGL si sta rivelando un po 'un problema. Ho le seguenti due file:Perché QGLWidget esegue solo il rendering di uno schermo vuoto?

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 

Quando compilo e si esegue questo, ottengo una finestra che ha tutto ciò che c'era dietro, al momento della creazione, impressa su di essa. Quello che mi aspetto è uno schermo rosso. Cosa mi manca?

UPDATE

ho modificato il mio implementazione GLWidget, e ho preso a lavorare. Tuttavia, funziona solo quando chiamo glDrawArrays (vedere la funzione paintGL di seguito). In SDL, glDrawArrays non era necessario per visualizzare uno schermo colorato vuoto. Senza glDrawArrays, qt sembra ignorare glClear() per qualche ragione. Qualcuno sa perché?

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

ho pensato che forse Qt stava facendo qualcosa subdolo sotto il cofano, e che se ho fatto tutto manualmente, vorrei sbarazzarsi del problema. Ma qt ancora in qualche modo sa se sto usando un programma o meno, e se sto usando glDrawArrays o no. Nel codice sottostante, eliminare glDrawArrays o glUseProgram rende il codice non funzionante. Deve avere qualcosa a che fare con ciò che accade all'interno di 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"); 
    } 
} 
+0

Il motivo potrebbe essere questo: QT docs per void QGLWidget :: swapBuffers(): > Scambia il contenuto dello schermo con un buffer fuori schermo. Funziona solo se il formato del widget specifica la modalità doppio buffer. > Normalmente, non è necessario chiamare questa funzione in modo esplicito perché viene eseguita automaticamente dopo ogni ridisegno del widget, ad esempio ogni volta dopo che paintGL() è stato eseguito. – rpress

+0

@rpress Ho giocato un po 'con questa idea, ma non penso che sia questo il problema. Vedi l'aggiornamento per maggiori dettagli. –

+0

@WilliamOliver era il problema originale - almeno, posso riprodurre lo "schermo vuoto" nel codice originale, e rendere lo schermo rosso desiderato * * * rimuovendo la chiamata a 'swapBuffers' o aggiungendo una chiamata a' setAutoBufferSwap (false) '. Non funzionava? – Lack

risposta

5

ho trovato la soluzione al mio problema. QGLWidget è deprecato. Chiunque veda questa domanda in futuro dovrebbe usare invece 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); 
} 

Questo codice funziona bene. L'unica differenza è che sto usando QOpenGLWidget invece di QGLWidget. È molto meglio di QGLWidget comunque, perché ridimensiona automaticamente la porta di visualizzazione e in realtà utilizza internamente due buffer di frame (apparentemente QGLWidget stava solo fingendo di usare due buffer).

+1

Non hai detto che stavi usando Qt5. –

+0

@FabienR Non lo ero, ma la documentazione dice di evitare l'uso di 4.8 –

+1

Direi che [QOpenGLWidget] (http://doc.qt.io/qt-5/qopenglwidget.html) è apparso in Qt5.4. –

0

Potrebbe essersi verificato un problema di configurazione sul PC.

Ecco l'esempio che uso su debian amd64/stable con Qt4.8.

intestazione

#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 

implementazione

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

    } 

L'unico problema che vedo è la creazione di un'altra finestra di proprietà di GLEW. Ma puoi chiuderlo. L'istanza QGLWidget viene promossa in basso a destra.

QGLWidget

Problemi correlati