Part 2 – Modern OpenGL Using Qt 5.5 Tutorial

Have a look at Part 1  if you are new to OpenGL or Qt. In Part 1 I introduced the two technologies and gave some important tips for getting started correctly. If you want to build this project with CMake and Qt Creator, here is a CMakeLists.txt file with instructions on how to do it.

Let’s get started!
This tutorial will show you how to draw a colored triangle similar to the QOpenGLWindow example that ships with Qt but using modern OpenGL. I don’t want to just explain the code, but instead plan on gathering all the little tips and gotchas I have found in order to really help you understand where to go from here.  I chose to develop for OpenGL version 3.1 because that is the max my laptop has and for anyone with money and interest to buy a 3D program they probably have that supported by their graphics card in their computer. 3.1 was released on March 24, 2009. Most graphics cards purchased after that will support 3.1. I will try and keep things as close to the existing examples as possible so that you can see exactly what changes when going from straight OpenGL to using Qt. This example will be broken down as follows.

-Subclass a Qt Window called OpenGLWindow from the Qt OpenGL Window Example
-put our OpenGL code in the initialization and render methods of our subclass
-create a QSurfaceFormat and set its RenderableType to OpenGL and with a version of 3.1
-create a window from our class and set its SurfaceFormat

The first things that need to be done to render OpenGL with Qt is make sure the Window will render at all the right times, make sure our OpenGL context is created at the right time and current and initialize the Qt OpenGL functions. All of this is taken care of in the OpenGLWinow class provided by the Qt example linked above. The one thing I changed was to inherit from QOpenGLFunctions_3_1. This way I will know for sure which functions Qt has for me to use (as opposed to just QOpenGLFunctions).

Initialize() method
The initialize() method will be for
-loading our shaders
-setting our vertices and colors (since they don’t change)
-setting up our VBOs for the corresponding vertex and color arrays
-adding our VBOs to a VAO

Render() method
The render() method will
-set our view scale
-glClear() color
-glDrawArrays to draw our VAO

Now that you know what to expect I will leave the rest of the tips as code comments so you know exactly what I am referring to.

main.cpp


#include <QtGui/QGuiApplication>
#include <QtGui/QMatrix4x4>
#include <QtGui/QScreen>
#include <QtCore/QMath>
#include "main.h";

int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);

QSurfaceFormat format;
format.setDepthBufferSize( 4 );
format.setSamples(24);
format.setVersion(3, 1);
format.setRenderableType(QSurfaceFormat::OpenGL);
//format.setProfile( QSurfaceFormat::CoreProfile );

ModernTriangle window;
window.setFormat(format);
window.resize(640, 480);
window.show();

return app.exec();
}
void ModernTriangle::initialize()
{
glClearColor(0.0f, 0.5f, 1.0f, 1.0f);

m_program = new QOpenGLShaderProgram();
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
#version 140\n //GLSL version 1.4
"in vec3 position;\n" //attribute named position with 3 elements per vertex in
"in vec3 color;\n"
"out vec4 fragColor;\n"
"void main() {\n"
" fragColor = vec4(fragColor, 1.0);\n"
" gl_Position = vec4(position, 1.0);\n"
"}\n"
);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
"#version 140\n" //GLSL version 1.4
"in vec3 fragColor;\n"
"out vec4 finalcolor;\n"
"void main() {\n"
" finalcolor = vec4(fragColor, 1.0);\n"
"}\n"
);

m_program->link();
m_program->bind(); // bind Shader (Do not release until VAO is created)

//location of vertex data arrays must be before they are referenced
//but location not important otherwise
static const float vertexPositions[] = {
-1.0f, 0.0f, 0.0f, //(x,y,z) bottom left
1.0f, 0.0f, 0.0f, //bottom right
0.0f, 1.0f, 0.0f //top middle
};

static const float vertexColors[] = {
1.0f, .0f, .0f, //red (r,g,b) values for each vertex
.0f, 1.0f, .0f, //green
.0f, .0f, 1.0f //blue
};

m_vao.create();
m_vao.bind(); //sets the Vertex Array Object current to the OpenGL context so we can write attributes to it

QOpenGLBuffer m_vvbo(QOpenGLBuffer::VertexBuffer);
m_vvbo.create();
m_vvbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vvbo.bind();
m_vvbo.allocate(vertexPositions, 9 * sizeof(float));
m_program->enableAttributeArray("position"); //this labels an attribute "position"
//that points to the memory slot from the last buffer allocate()
//the position attribute is an input to our vertex shader
m_program->setAttributeBuffer("position", GL_FLOAT, 0, 3);

QOpenGLBuffer m_vcbo(QOpenGLBuffer::VertexBuffer);
m_vcbo.create();
m_vcbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vcbo.bind();
m_vcbo.allocate(vertexColors, 9 * sizeof(float));
m_program->enableAttributeArray("color"); //this labels an attribute "color"
//that points to the memory slot from the last buffer allocate()
//the color attribute is an input to our vertex shader
m_program->setAttributeBuffer("color", GL_FLOAT, 0, 3);

// Release (unbind) all
m_vvbo.release();
m_vcbo.release();
m_vao.release();
m_program->release();
}
void ModernTriangle::render()
{
const qreal retinaScale = devicePixelRatio();
glViewport(0, 0, width() * retinaScale, height() * retinaScale);

// Clear
glClear(GL_COLOR_BUFFER_BIT);

// Render using our shader
m_program->bind();
m_vao.bind(); //sets
glDrawArrays(GL_TRIANGLES, 0, 3);
m_vao.release();
m_program->release();
}

main.h


#include "openglwindow.h"
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLBuffer>
#include <QtGui/QOpenGLVertexArrayObject>

//header style declaration
class ModernTriangle : public OpenGLWindow
{
public:
ModernTriangle();

void initialize() Q_DECL_OVERRIDE;
void render() Q_DECL_OVERRIDE;

private:
GLuint vertexLocation;
GLuint colorLocation;
GLuint matrixLocation;

QOpenGLVertexArrayObject m_vao; // Our Vertex Array Object
QOpenGLBuffer m_vvbo; // Our vertice Vertex Buffer Object
QOpenGLBuffer m_vcbo; // Our color Vertex Buffer Object

QOpenGLShaderProgram* m_program;
};

ModernTriangle::ModernTriangle()
{
}

openglwindow.cpp


/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "openglwindow.h"

#include <QtCore/QCoreApplication>

#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLPaintDevice>
#include <QtGui/QPainter>

//! [1]
OpenGLWindow::OpenGLWindow(QWindow *parent)
: QWindow(parent)
, m_update_pending(false)
, m_animating(false)
, m_context(0)
, m_paint_device(0)
{
setSurfaceType(QWindow::OpenGLSurface);
}

//! [1]

OpenGLWindow::~OpenGLWindow()
{
delete m_paint_device;
}
//! [2]
void OpenGLWindow::render(QPainter *painter)
{
Q_UNUSED(painter);
}

void OpenGLWindow::initialize()
{
}

void OpenGLWindow::render()
{
if (!m_paint_device)
m_paint_device = new QOpenGLPaintDevice;

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

m_paint_device->setSize(size());

QPainter painter(m_paint_device);
render(*painter);
}
//! [2]

//! [3]
void OpenGLWindow::renderLater()
{
if (!m_update_pending) {
m_update_pending = true;
QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
}
}

bool OpenGLWindow::event(QEvent *event)
{
switch (event->type()) {
case QEvent::UpdateRequest:
m_update_pending = false;
renderNow();
return true;
default:
return QWindow::event(event);
}
}

void OpenGLWindow::exposeEvent(QExposeEvent *event)
{
Q_UNUSED(event);

if (isExposed())
renderNow();
}
//! [3]

//! [4]
void OpenGLWindow::renderNow()
{
if (!isExposed())
return;

bool needsInitialize = false;

if (!m_context) {
m_context = new QOpenGLContext(this);
m_context->setFormat(requestedFormat());
m_context->create();
needsInitialize = true;
}
m_context->makeCurrent(this);

if (needsInitialize) {
initializeOpenGLFunctions();
initialize();
}

render();
m_context->swapBuffers(this);
if (m_animating)
renderLater();
}
//! [4]

void OpenGLWindow::setAnimating(bool animating)
{
m_animating = animating;

if (animating)
renderLater();
}

openglwindow.h


/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtGui/QWindow>
#include <QtGui/QOpenGLFunctions_3_1>

QT_BEGIN_NAMESPACE
class QPainter;
class QOpenGLContext;
class QOpenGLPaintDevice;
QT_END_NAMESPACE

//! [1]
class OpenGLWindow : public QWindow, protected QOpenGLFunctions_3_1
{
Q_OBJECT
public:
explicit OpenGLWindow(QWindow *parent = 0);
~OpenGLWindow();

virtual void render(QPainter *painter);
virtual void render();

virtual void initialize();

void setAnimating(bool animating);

public slots:
void renderLater();
void renderNow();

protected:
bool event(QEvent *event) Q_DECL_OVERRIDE;

void exposeEvent(QExposeEvent *event) Q_DECL_OVERRIDE;

private:
bool m_update_pending;
bool m_animating;

QOpenGLContext *m_context;
QOpenGLPaintDevice *m_paint_device;
};
//! [1]

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s