C++ - Qt Framework - Mixing native OpenGL functions with Qt ones

Having the Qt Framework as GUI for OpenGL can be helpful of course but sometimes it seems easier to use the native OpenGL functions directly.

This is exactly what we are going to see in this tutorial by mixing Qt and native OpenGL functions.

First to all

For this tutorial recipe we are going to use:

  • 2 different pairs of shaders (vertex and fragment) 
  • 2 VAOs
  • 2 VBOs
  • 2 EBOs

Shaders will be placed in the "shader/" directory and other files directly at the root.

The mix will consist of getting the QOpenGLShaderProgram class from Qt.

And of course the classic initializeGL(), resizeGL() and paintGL() from the QOpenGLWidget class.

Other than that we'll get everything from the native OpenGL functions.

As we need 2 pairs of shaders we'll create and link them with 2 methods, each one creating a program linked with the 2 compiled shaders (vertex and fragment).

These 2 methods are initProgram1 and initProgram2.

Then we create 2 methods in order to create our 2 VAOs with their VBO and EBO.

That is vaoCreation1 and vaoCreation2 which use their own vector of vertexes and indexes.

In these methods you can see that only vaoCreation2 uses 2 layout attributes (location = 0 and location = 1) in order to deal with positions and colors directly from the vector of vertexes.

The vaoCreation1 manages the colors from another way: with a uniform attribute.

Indeed, from the paintGL methods we check which program is currently running.

If it's program1 then we change the color of the shape with random numbers.

So it changes the colors directly from our code, not from the vector of vertexes anymore.

But when the program2 takes the lead we tells OpenGL to check which shader is activated.

And from the fragment2 shader we have a "if" statement which says that if the color is black (meaning no colors at all) we change this color to white.

But this is only true when the VAO1 is activated with the shader2 because there is no layoutColor in this case (location 1).

When the VAO2 is activated with the shader2 the color isn't black anymore, we use indeed the attribute layoutColor to retrieve colors from the vector of indexes.

Of course we use the keyboard to switch from shader1 to shader2 then from VAO1 to VAO2.

The keys are:

  • 1 = shader1
  • 2 = shader2
  • J = VAO1
  • K = VAO2

Moreover when we are with VAO1 we set the glPolygonMode to FILL and when with VAO2 we set to LINE.

To resume:

  • VAO1 and shader1 makes the shape changes with many colors
  • VAO1 and shader2 makes the shape changes to white
  • VAO2 and shader1 makes the shape changes with many colors
  • VAO2 and shader2 makes the shape changes with colors taken from the vector of indexes.

Let's code a bit

BadprogMixQtOpenGL.cpp

#include "BadprogMixQtOpenGL.h"
#include "BadprogWidget.h"

#include <QDebug>

// badprog.com

BadprogMixQtOpenGL::BadprogMixQtOpenGL(QWidget* parent)
  : QMainWindow(parent)
{
  //
  ui.setupUi(this);

  //
  BadprogWidget* bpWidget = new BadprogWidget(this);

  //
  setWindowTitle("Badprog - Mixing native OpenGL and Qt functions");

  //
  setCentralWidget(bpWidget);
}

 

 

BadprogMixQtOpenGL.h

#ifndef BADPROG_MIX_QT_OPENGL_H
#define BADPROG_MIX_QT_OPENGL_H

#include <QtWidgets/QMainWindow>
#include "ui_BadprogMixQtOpenGL.h"

// badprog.com

class BadprogMixQtOpenGL : public QMainWindow
{
    Q_OBJECT

public:
    BadprogMixQtOpenGL(QWidget *parent = Q_NULLPTR);

private:
    Ui::BadprogMixQtOpenGL ui;
};

#endif // !BADPROG_MIX_QT_OPENGL_H

 

BadprogMixQtOpenGL.qrc

<RCC>
    <qresource prefix="">

    <file alias="vertex_color1"     >./shader/shader_vertex_color1.glsl</file>
    <file alias="fragment_color1"   >./shader/shader_fragment_color1.glsl</file>
    <file alias="vertex_color2"     >./shader/shader_vertex_color2.glsl</file>
    <file alias="fragment_color2"   >./shader/shader_fragment_color2.glsl</file>

    </qresource>
</RCC>

BadprogMixQtOpenGL.ui

<UI version="4.0" >

 <class>BadprogMixQtOpenGL</class>

 <widget class="QMainWindow" name="BadprogMixQtOpenGL" >

  <property name="objectName" >

   <string notr="true">BadprogMixQtOpenGL</string>

  </property>

  <property name="geometry" >

   <rect>

    <x>0</x>

    <y>0</y>

    <width>600</width>

    <height>400</height>

   </rect>

  </property>

  <property name="windowTitle" >

   <string>BadprogMixQtOpenGL</string>

  </property>  <widget class="QMenuBar" name="menuBar" />

  <widget class="QToolBar" name="mainToolBar" />

  <widget class="QWidget" name="centralWidget" />

  <widget class="QStatusBar" name="statusBar" />

 </widget>

 <layoutDefault spacing="6" margin="11" />

 <pixmapfunction></pixmapfunction>

 <resources>

   <include location="BadprogMixQtOpenGL.qrc"/>

 </resources>

 <connections/>

</UI>
 

BadprogWidget.cpp

#include "BadprogWidget.h"

#include <QOpenGLShaderProgram>
#include <QKeyEvent>

// badprog.com

//
#define MAX(a, b) (a) > (b) ? (a) : (b)

// ----------------------------------------------------------------------------
// CTOR
// ----------------------------------------------------------------------------
BadprogWidget::BadprogWidget(QWidget* parent)
  : QOpenGLWidget(parent)
{
  //
  _vectorOfVertex1 = {
   0.5f,  0.5f, 0.0f,
   0.5f, -0.5f, 0.0f,
  -0.5f, -0.5f, 0.0f,
  -0.5f,  0.5f, 0.0f
};

  //
  _vectorOfVertex2 = {
  // position           // color RGB
  -0.2f, -0.2f, 0.0f,   1.0f, 0.0f, 0.0f,
  -0.3f, -0.3f, 0.0f,   0.0f, 1.0f, 1.0f,
  0.5f, 0.2f, 0.0f,     0.0f, 1.0f, 1.0f,
  0.0f,  0.0f, 0.0f,    1.0f, 0.5f, 0.5f,
  0.5f,  -0.5f, 0.0f,   1.0f, 1.0f, 1.0f
};

  //
  _vectorOfIndex1 = {
  0, 1, 3,  
  1, 2, 3    
};

  //
  _vectorOfIndex2 = {
  0, 1, 2,
  1, 2, 3,
  3, 4, 1,
  2, 1, 4,
};

  //
  setFocusPolicy(Qt::StrongFocus);

  //
  _maxSizeArray = MAX(_vectorOfIndex1.size(), _vectorOfIndex2.size());

  //
  _programMain = nullptr;
  _modePolygon = GL_FILL;
}

// ----------------------------------------------------------------------------
// DTOR
// ----------------------------------------------------------------------------
BadprogWidget::~BadprogWidget() {}
// ----------------------------------------------------------------------------
// 1. initializeGL
// ----------------------------------------------------------------------------
void BadprogWidget::initializeGL() {
  //
  initializeOpenGLFunctions();

  //
  initProgram1();
  initProgram2();

  //
  vaoCreation1();
  vaoCreation2();

  //
  _programMain = _program1;
  _vaoMain = _vao1;
}

// ----------------------------------------------------------------------------
// 2. resizeGL
// ----------------------------------------------------------------------------
void BadprogWidget::resizeGL(int width, int height) {
  //
  glViewport(0, 0, width, height);
}

// ----------------------------------------------------------------------------
// 3. paintGL
// ----------------------------------------------------------------------------
void BadprogWidget::paintGL() {
  //
  glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
  glClear(GL_COLOR_BUFFER_BIT);

  //
  glPolygonMode(GL_FRONT_AND_BACK, _modePolygon);

  //
  _programMain->bind();

  //
  if (_programMain == _program1) {
    //
    int randInt1 = rand();
    int randInt2 = rand();
    int randInt3 = rand();
    int randInt4 = rand();
   
    //
    float randFloat1 = (float)randInt1;
    float randFloat2 = (float)randInt2;
    float randFloat3 = (float)randInt3;
    float randFloat4 = (float)randInt4;

    //
    randFloat1 /= RAND_MAX;
    randFloat2 /= RAND_MAX;
    randFloat3 /= RAND_MAX;
    randFloat4 /= RAND_MAX;

    //
    unsigned int uniformLocation = glGetUniformLocation(_programMain->programId(), "uniformColor");

    //
    glUniform4f(uniformLocation, randFloat1, randFloat2, randFloat3, randFloat4);
    update();
  }

  //
  glBindVertexArray(_vaoMain);
  glDrawElements(GL_TRIANGLES, _maxSizeArray, GL_UNSIGNED_INT, 0);
}

// ============================================================================
// initProgram1
// ============================================================================
void BadprogWidget::initProgram1() {
  // Program.
  _program1 = new QOpenGLShaderProgram(this);
  _program1->addShaderFromSourceFile(QOpenGLShader::Vertex, ":vertex_color1");
  _program1->addShaderFromSourceFile(QOpenGLShader::Fragment, ":fragment_color1");

  //
  _program1->link(); // linking
}
// ============================================================================
// initProgram2
// ============================================================================
void BadprogWidget::initProgram2() {
  // Program.
  _program2 = new QOpenGLShaderProgram(this);
  _program2->addShaderFromSourceFile(QOpenGLShader::Vertex, ":vertex_color2");
  _program2->addShaderFromSourceFile(QOpenGLShader::Fragment, ":fragment_color2");

  //
  _program2->link(); // linking
}

// ----------------------------------------------------------------------------
// vaoCreation1
// ----------------------------------------------------------------------------
void BadprogWidget::vaoCreation1() {
  // VAO 1
  glGenVertexArrays(1, &_vao1);
  glBindVertexArray(_vao1);

  // VBO 1 with the source array
  glGenBuffers(1, &_vbo1);
  glBindBuffer(GL_ARRAY_BUFFER, _vbo1);
  glBufferData(GL_ARRAY_BUFFER, _vectorOfVertex1.size() * sizeof(float), &_vectorOfVertex1.front(), GL_STATIC_DRAW);

  // EBO 1 with the index array
  glGenBuffers(1, &_ebo1);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo1);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, _vectorOfIndex1.size() * sizeof(float), &_vectorOfIndex1.front(), GL_STATIC_DRAW);

  // Location 0
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (void*)0);
  // Activate the index "0" (location)
  glEnableVertexAttribArray(0);
}

// ----------------------------------------------------------------------------
// vaoCreation2
// ----------------------------------------------------------------------------
void BadprogWidget::vaoCreation2() {
  // VAO 2
  glGenVertexArrays(1, &_vao2);
  glBindVertexArray(_vao2);

  // VBO 2 with the source array
  glGenBuffers(1, &_vbo2);
  glBindBuffer(GL_ARRAY_BUFFER, _vbo2);
  glBufferData(GL_ARRAY_BUFFER, _vectorOfVertex2.size() * sizeof(float), &_vectorOfVertex2.front(), GL_STATIC_DRAW);

  // EBO 2 with the index array
  glGenBuffers(1, &_ebo2);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo2);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, _vectorOfIndex2.size() * sizeof(float), &_vectorOfIndex2.front(), GL_STATIC_DRAW);

  // Location 0
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)0);
  // Activate the index "0" (location)
  glEnableVertexAttribArray(0);

  // Location 1 from Shader vertex 2
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)(3 * sizeof(GL_FLOAT)));
  // Activate the index "1" (color)
  glEnableVertexAttribArray(1);
}

// ============================================================================
// keyPressEvent
// ============================================================================
void BadprogWidget::keyPressEvent(QKeyEvent* event) {
  //
  if (Qt::Key_1 == event->key()) {
    _programMain = _program1;
    update();
  }
  else if (Qt::Key_2 == event->key()) {
    _programMain = _program2;
    update();
  }

  //
  if (Qt::Key_J == event->key()) {
    _vaoMain = _vao1;
    _modePolygon = GL_FILL;
    update();
  }
  else if (Qt::Key_K == event->key()) {
    _vaoMain = _vao2;
    _modePolygon = GL_LINE;
    update();
  }
}
 

BadprogWidget.h

#ifndef BADPROG_WIDGET_H
#define BADPROG_WIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_4_5_Core>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>

// badprog.com

class QKeyEvent;

class BadprogWidget : public QOpenGLWidget, protected QOpenGLFunctions_4_5_Core
{
  Q_OBJECT

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

  //
public:
  void initProgram1();
  void initProgram2();
  void vaoCreation1();
  void vaoCreation2();
  //
protected:
  void initializeGL()                   override;
  void paintGL()                        override;
  void resizeGL(int width, int height)  override;

  // Override
  void keyPressEvent(QKeyEvent* event) override;

  //
private:
  QOpenGLShaderProgram* _programMain;
  QOpenGLShaderProgram* _program1;
  QOpenGLShaderProgram* _program2;

  // VAO
  GLuint _vaoMain;
  GLuint _vao1;
  GLuint _vao2;

  // VBO
  GLuint _vbo1;
  GLuint _vbo2;

  // EBO
  GLuint _ebo1;
  GLuint _ebo2;

  //
  std::vector<float> _vectorOfVertex1;
  std::vector<float> _vectorOfVertex2;
  std::vector<unsigned int> _vectorOfIndex1;
  std::vector<unsigned int> _vectorOfIndex2;
  int _maxSizeArray;
  GLint _modePolygon;
};

#endif // !BADPROG_WIDGET_H

 

main.cpp

#include "BadprogMixQtOpenGL.h"
#include <QtWidgets/QApplication>

// badprog.com

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    BadprogMixQtOpenGL w;
    w.show();
    return a.exec();
}

 

shader/shader_fragment_color1.glsl

#version 330 core

// badprog.com
// out
out vec4 FragColor;

// in
uniform vec4 uniformColor;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    FragColor = uniformColor;
}

 

shader/shader_fragment_color2.glsl

#version 330 core

// badprog.com
// out
out vec4 FragColor;

// in
in vec3 shaderColor;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    FragColor = vec4(shaderColor, 1.0f);
}

 

shader/shader_vertex_color1.glsl

#version 330 core

// badprog.com
// layout
layout (location = 0) in vec3 layoutPosition;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    gl_Position = vec4(layoutPosition, 1.0f);

}

 

shader/shader_vertex_color2.glsl

#version 330 core

// badprog.com
// layout
layout (location = 0) in vec3 layoutPosition;
layout (location = 1) in vec3 layoutColor;

// out
out vec3 shaderColor;
out vec3 colorWhite;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    colorWhite.x = 1.0f;
    colorWhite.y = 1.0f;
    colorWhite.z = 1.0f;

    //
    gl_Position = vec4(layoutPosition, 1.0f);
    shaderColor = layoutColor;
   
    //
    if (shaderColor.x <= 0.0f && shaderColor.y <= 0.0f && shaderColor.z <= 0.0f) {
        shaderColor = colorWhite.xyz;
    }
}

 

Conclusion

What's interesting here is to have the GUI completely managed by Qt and we don't have to bother with  different libraries with their esoteric include files.

Furthermore it's totally possible to pick which part of your program you'll make with native OpenGL functions or with Qt ones.

Thank you for reaching this line, I hope you enjoyed this tutorial.

See you for another tutorial cool

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.