简体   繁体   中英

What is the issue with this QOpenGLWidget?

Why/how do I bind vertex attributes to a shader? - examples do but never explain.

If I allow a vertex attribute in GLSL location = 0, does that still have to be allowed in code?

How to properly set Q-vertex array buffers/objects?

Where does this* weird artifact even come from? 在此处输入图像描述在此处输入图像描述

*The blue square is a label used to display QPixmap images. The green one is the 1 GLWidget that inherits from QOpenGLWidget. As you can see, it creates a weird square on the top-left aswell.

The Green window is supposed to display a red triangle on the lower right corner.

The relevant code might only be the last section, GLWidget.h.

Also paintGL() only runs just a few times for some reason. code:

.pro:

QT       += core gui opengl

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11 console

LIBS += -lopengl32

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    GLWidget.h \
    mainwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

main:

#include <iostream>
#include <cstdlib>
#include "mainwindow.h"
#include <QApplication>
#include <QGLFormat>
#include <qimagereader.h>
#include "GLWidget.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 3);
    format.setProfile(QSurfaceFormat::CoreProfile);
    //format.setSampleBuffers(true); - throws undefined
    QSurfaceFormat::setDefaultFormat(format);
    std::cout<<"\nContext set up!";

    MainWindow w;
    GLWidget g(&w);
    w.show();

    return a.exec();
}

Mainwindow.h

#include <QMainWindow>
#include <QPixmap>
#include <QDir>
#include <QFileDialog>
#include <QListWidgetItem>
#include <map>


QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:

    void on_openButton_clicked();

    void on_closeButton_clicked();

    void on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);

private:
    Ui::MainWindow *ui;
    std::map<QString, QPixmap> pictures;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->label_pic->setStyleSheet("QLabel { background-color : blue; color: white;}");
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_openButton_clicked()
{
    QString file_name = "";
    file_name = QFileDialog::getOpenFileName(this, "Open a File", QDir::homePath());
    if(file_name == "") return;
    QPixmap map(file_name);

    ///chopping just the filename
    for(unsigned i = file_name.length()-1; i > 0; i--){
        if(file_name[i] == '/'){
            file_name = file_name.mid(i+1);
            break;
        }
    }
    ui->listWidget->addItem(file_name);
    pictures.insert(std::pair<QString, QPixmap>(file_name, map));
}



void MainWindow::on_closeButton_clicked()
{
    QString item = ui->listWidget->currentItem()->text();
    std::map<QString, QPixmap>::iterator It = pictures.find(item);

    if( It != pictures.end() ) pictures.erase( It );
    QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
    foreach(QListWidgetItem * item, items)
    {
        delete ui->listWidget->takeItem(ui->listWidget->row(item));
    }
}


void MainWindow::on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{
    QString item = ui->listWidget->currentItem()->text();
    ui->label_pic->setPixmap(pictures.at(item));
}

GLWidget.h

#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLContext>
#include <QSurface>
#include <QWindow>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShader>            //for compiling
#include <QOpenGLShaderProgram>     //for link and use
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QMatrix>
#include <QGLFormat>

#include <iostream>
#include <cstdlib>

const QString vertex_src =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
    "gl_Position = vec4(aPos, 1.0);\n"
"}\n";

const QString fragment_src =
"#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
    "FragColor = FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"
"}\n";

const GLfloat vertices[] = {

    1.0f, 1.0f, -4.0f,
    -1.0f, -1.0f, -4.0f,
    1.0f, -1.0f, -4.0f,

    1.0f, 1.0f, 0.0f,
    -1.0f, -1.0f, 0.0f,
    1.0f, -1.0f, 0.0f,

    1.0f, 1.0f, 4.0f,
    -1.0f, -1.0f, 4.0f,
    1.0f, -1.0f, 4.0f

};

class GLWidget : public QOpenGLWidget
{
private:
        QOpenGLShader *fragmentShader;
        QOpenGLShader *vertexShader;
        QOpenGLShaderProgram *shaderProgram;
        QOpenGLFunctions_3_3_Core *f;
        //QOpenGLFunctions *f;
        QOpenGLVertexArrayObject VAO;
        QOpenGLBuffer VBO;

public:
    GLWidget(QWidget *parent = 0) : QOpenGLWidget(parent) {
        std::cout<<"\nGLWidget lives";
    }

protected:
    void initializeGL() override
    {
        connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup);
        //initializeOpenGLFunctions<QT_OPENGL_3_3_FUNCTIONS>();
        //f = QOpenGLFunctions::initializeOpenGLFunctions();
        // Set up the rendering context, load shaders and other resources, etc.:

        //this->setFormat(QSurfaceFormat::defaultFormat());
        //f = QOpenGLContext::currentContext()->functions();
        f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
        f->glClearColor(0.0f, 0.2f, 0.0f, 1.0f);
        f->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        f->glDisable(GL_CULL_FACE);

        vertexShader = new QOpenGLShader ( QOpenGLShader :: Vertex );
        if (!vertexShader->compileSourceCode(vertex_src) ) {std::cout<<"\ncould not compile vertex shader"; exit(1);}

        fragmentShader = new QOpenGLShader( QOpenGLShader :: Fragment ) ;
        if (! fragmentShader->compileSourceCode(fragment_src) ) {std::cout<<"\ncould not compile fragment shader"; exit(1);}

        shaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
        shaderProgram->addShader( vertexShader );
        shaderProgram->addShader( fragmentShader );
        shaderProgram->link();
        //shaderProgram->create();
        shaderProgram->bind();

        shaderProgram->enableAttributeArray("aPos");
        shaderProgram->setAttributeArray("aPos", &vertices[0], 3, 0);

        /*
        VAO = new QOpenGLVertexArrayObject(this);
        VAO->create();
        VAO->bind();

        VBO = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
        VBO->create();
        VBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
        VBO->bind();
        VBO->allocate(&vertices[0], 9);
        m_funcs->glEnableVertexAttribArray(0);
        m_funcs->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3, (void*)0);
        shaderProgram->enableAttributeArray(0);
        shaderProgram->setAttributeBuffer("aPos", GL_FLOAT, 0, 3, 0);
          */

        VAO.create();
        if (VAO.isCreated()) VAO.bind();
        else {std::cout<<"\nCould not bind VAO"; exit(1);}

        VBO = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
        VBO.create();
        VBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
        if (VBO.isCreated()) VBO.bind();
        else {std::cout<<"\nCould not bind VBO"; exit(1);}
        VBO.allocate(&vertices[0], 9);

        f->glEnableVertexAttribArray(0);
        f->glVertexAttribPointer(0, 27 * sizeof(float), GL_FLOAT, GL_FALSE, 0, (void*)0);


    }

    void resizeGL(int w, int h) override
    {
        // Update projection matrix and other size related settings:
        // m_projection.setToIdentity();
        // m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f);
        glViewport( 0, 0, w, qMax( h, 1 ) );
    }

    void paintGL() override
    {
        //QOpenGLFunctions_3_3_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
        // Draw the scene:
        std::cout<<"\npaint runs";
        f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFramebufferObject());
        f->glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

        if(shaderProgram->bind()) {std::cout<<"\n\tshader bound";}

        if(VBO.bind()) {std::cout<<"\n\tVAO bound";}
        f->glDrawArrays(GL_TRIANGLES, 0, 9);
    }
public:
    void cleanup()
    {
        makeCurrent();
        delete vertexShader;
        delete fragmentShader;
        delete shaderProgram;
        VAO.destroy();
        VBO.destroy();
        doneCurrent();
    }

    ~GLWidget()
    {
        cleanup();
    }

};
#endif // GLWIDGET_H

(The triangle vertices exist in -1, 0 and 1 Z-positions. I didn't want to draw them behind the view pane.)

UI.xml

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>718</width>
    <height>472</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QLabel" name="label_pic">
    <property name="geometry">
     <rect>
      <x>170</x>
      <y>20</y>
      <width>211</width>
      <height>61</height>
     </rect>
    </property>
    <property name="maximumSize">
     <size>
      <width>501</width>
      <height>431</height>
     </size>
    </property>
    <property name="layoutDirection">
     <enum>Qt::LeftToRight</enum>
    </property>
    <property name="autoFillBackground">
     <bool>false</bool>
    </property>
    <property name="text">
     <string>Picture Goes Here (or not)</string>
    </property>
    <property name="alignment">
     <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
    </property>
   </widget>
   <widget class="QPushButton" name="openButton">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>20</y>
      <width>161</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>Open File..</string>
    </property>
   </widget>
   <widget class="QPushButton" name="closeButton">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>430</y>
      <width>161</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>Close File</string>
    </property>
   </widget>
   <widget class="QListWidget" name="listWidget">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>50</y>
      <width>161</width>
      <height>371</height>
     </rect>
    </property>
   </widget>
   <widget class="GLWidget" name="openGLWidget">
    <property name="geometry">
     <rect>
      <x>310</x>
      <y>100</y>
      <width>281</width>
      <height>261</height>
     </rect>
    </property>
   </widget>
  </widget>
 </widget>
 <customwidgets>
  <customwidget>
   <class>GLWidget</class>
   <extends>QOpenGLWidget</extends>
   <header location="global">GLWidget.h</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

The second parameter of f->glVertexAttribPointer(0, 27 * sizeof(float), GL_FLOAT, GL_FALSE, 0, (void*)0); must be the number of components (values 1..4) I have made several minor changes, but this code runs, and produces a triangle. Bear the unnecessary includes. I don't know which ones are actually needed.

#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLContext>
#include <QSurface>
#include <QWindow>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShader>            //for compiling
#include <QOpenGLShaderProgram>     //for link and use
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QMatrix>
#include <QGLFormat>

#include <iostream>
#include <cstdlib>

const QString vertex_src =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
    "gl_Position = vec4(aPos, 1.0);\n"
"}\n";

const QString fragment_src =
"#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
    "FragColor = FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"
"}\n";

const GLfloat vertices[] = {

    1.0f, 1.0f, -4.0f,
    -1.0f, -1.0f, -4.0f,
    1.0f, -1.0f, -4.0f,

    1.0f, 1.0f, 0.0f,
    -1.0f, -1.0f, 0.0f,
    1.0f, -1.0f, 0.0f,

    1.0f, 1.0f, 4.0f,
    -1.0f, -1.0f, 4.0f,
    1.0f, -1.0f, 4.0f

};

class GLWidget : public QOpenGLWidget
{
private:
        QOpenGLShader *fragmentShader;
        QOpenGLShader *vertexShader;
        QOpenGLShaderProgram *shaderProgram;
        QOpenGLFunctions_3_3_Core *f;
        //QOpenGLFunctions *f;
        QOpenGLVertexArrayObject VAO;
        QOpenGLBuffer VBO;

public:
    GLWidget(QWidget *parent = 0) : QOpenGLWidget(parent) {
        std::cout<<"\nGLWidget lives";
    }

protected:
    void initializeGL() override
    {
        connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup);
        //f = new QOpenGLFunctions_3_3_Core();
        //f->initializeOpenGLFunctions(); "There is no need to call QAbstractOpenGLFunctions::initializeOpenGLFunctions() as long as versionFunctions context is current"
        f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
        f->glClearColor(0.0f, 0.2f, 0.0f, 1.0f);
        f->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        f->glDisable(GL_CULL_FACE);

        vertexShader = new QOpenGLShader ( QOpenGLShader :: Vertex );
        if (!vertexShader->compileSourceCode(vertex_src) ) {std::cout<<"\ncould not compile vertex shader"; exit(1);}

        fragmentShader = new QOpenGLShader( QOpenGLShader :: Fragment ) ;
        if (! fragmentShader->compileSourceCode(fragment_src) ) {std::cout<<"\ncould not compile fragment shader"; exit(1);}

        shaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
        shaderProgram->addShader( vertexShader );
        shaderProgram->addShader( fragmentShader );
        shaderProgram->link();
        //shaderProgram->create(); no need, just gives back a program id.
        shaderProgram->bind();


        shaderProgram->setAttributeArray("aPos", &vertices[0], 3, 0);
        shaderProgram->enableAttributeArray("aPos");

        VAO.create();
        if (VAO.isCreated()) VAO.bind();
        else {std::cout<<"\nCould not bind VAO"; exit(1);}

        VBO = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
        VBO.create();
        VBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
        if (VBO.isCreated()) VBO.bind();
        else {std::cout<<"\nCould not bind VBO"; exit(1);}
        VBO.allocate(&vertices[0], 27 * sizeof(float));
        //VBO.allocate(&vertices[0], 27);

        f->glEnableVertexAttribArray(0);
        f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
        //f->glVertexAttribPointer(0, 27, GL_FLOAT, GL_FALSE, 0, (void*)0);

        std::cout<<"\nGL context version "<<QOpenGLContext::currentContext()->format().majorVersion();
        std::cout<<"."<<QOpenGLContext::currentContext()->format().minorVersion();
        std::cout<<"\nGL context profile "<<QOpenGLContext::currentContext()->format().profile();
        std::cout<<"\n\tis that Core? ";
        if(QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) std::cout<<"True";
        else std::cout<<"False";

    }

    void resizeGL(int w, int h) override
    {
        // Update projection matrix and other size related settings:
        // m_projection.setToIdentity();
        // m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f);
        //glViewport( 0, 0, w, qMax( h, 1 ) );
        std::cout<<"\nResize called";
        glViewport( 0, 0, 800, 600 );
    }

    void paintGL() override
    {

        std::cout<<"\npaint runs";
        f->glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

        if(shaderProgram->bind()) {std::cout<<"\n\tshader bound";}

        if(VBO.bind()) {std::cout<<"\n\tVAO bound";}
        f->glDrawArrays(GL_TRIANGLES, 0, 9);
        QOpenGLContext::currentContext()->swapBuffers(QOpenGLContext::currentContext()->surface());
    }
public:
    void cleanup()
    {
        makeCurrent();
        delete vertexShader;
        delete fragmentShader;
        delete shaderProgram;
        VAO.destroy();
        VBO.destroy();
        doneCurrent();
    }

    ~GLWidget()
    {
        cleanup();
    }

};

#endif // GLWIDGET_H

Notice that in Qt, shaders also need to have vertex attributes enabled in contrast to the native implementation.(?)

Heres the updated main aswell in case someone needs it

#include <iostream>
#include <cstdlib>
#include "mainwindow.h"
#include <QApplication>
#include <QGLFormat>
#include <qimagereader.h>
#include "GLWidget.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 3);
    format.setProfile(QSurfaceFormat::CoreProfile);
    //format.setSampleBuffers(true); - throws undefined
    QSurfaceFormat::setDefaultFormat(format);
    std::cout<<"\nContext set up!";

    MainWindow w;
    w.show();

    return a.exec();
}

Also note that i enabled a setting called "run in terminal" in Qt, so i have a visible console. That's where the cout-s go.

The top-left square IS your widget rendered first time, when you added it in main() . As you added it manually, it's origin coords are (0,0) by default. The green square is is second one, created in UI, from template compiled out of XML definition.

painGL() is called when paintEvent() is called which can be triggered manually through calling update(0 or is triggered when Qt "feels" need to update widgets (eg something was drawn other them, they were resized, etc.).

Afaik, if you rebind program you have to rebind everything. And order of your binding and leaving objects active is peculiar.. along with fact that you're obviously going out of clipping space without having a projection matrix.

Order I'm used to is something like

void initializeGL() override
{
    connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup);

    f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
    f->glClearColor(0.0f, 0.2f, 0.0f, 1.0f);
    f->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    f->glDisable(GL_CULL_FACE);

    vertexShader = new QOpenGLShader ( QOpenGLShader :: Vertex );
    if (!vertexShader->compileSourceCode(vertex_src) ) {std::cout<<"\ncould not compile vertex shader"; exit(1);}

    fragmentShader = new QOpenGLShader( QOpenGLShader :: Fragment ) ;
    if (! fragmentShader->compileSourceCode(fragment_src) ) {std::cout<<"\ncould not compile fragment shader"; exit(1);}

    shaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
    shaderProgram->addShader( vertexShader );
    shaderProgram->addShader( fragmentShader );
    shaderProgram->link();


    shaderProgram->bind();

    VAO.create();
    if (VAO.isCreated()) VAO.bind();
        else {std::cout<<"\nCould not bind VAO"; exit(1);}

    VBO = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    VBO.create();
    if (VBO.isCreated()) VBO.bind();
       else {std::cout<<"\nCould not bind VBO"; exit(1);}
       VBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
       VBO.allocate(&vertices[0], 9*3*sizeof(float));

       shaderProgram->setAttributeArray("aPos", &vertices[0], 3, 0);
       shaderProgram->enableAttributeArray("aPos");
       // either
       shaderProgram->setAttributeBuffer("aPos",GL_FLOAT,0,3,0);
       // or?
       // f->glEnableVertexAttribArray(0);
       // f->glVertexAttribPointer(0, 9, GL_FLOAT, GL_FALSE, 0, (void*)0);
    VAO.release();

    // unbind when VAO is inactive.
    shaderProgram->disableAttributeArray("aPos");
    VBO.release();

    shaderProgram->release();

    std::cout<<"\nGL context version "<<QOpenGLContext::currentContext()->format().majorVersion();
            std::cout<<"."<<QOpenGLContext::currentContext()->format().minorVersion();
            std::cout<<"\nGL context profile "<<QOpenGLContext::currentContext()->format().profile();
            std::cout<<"\n\tis that Core? ";
            if(QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) std::cout<<"True";
            else std::cout<<"False";
}


void paintGL() override
{
    // Draw the scene:
    std::cout<<"\npaint runs";

    f->glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    shaderProgram->bind();
    VAO.bind();

    f->glDrawArrays(GL_TRIANGLES, 0, 9);

    VAO.release();
    shaderProgram->release();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM