Qt5 OpenGL Part 0: Creating a Window 23


Recently I enrolled in a class for Advanced Rendering Techniques. In order to make the class more interesting, I’ve paired it with learning more about the Qt Framework.

Now is a good time to start learning Qt, because lots of OpenGL functionality is evolving within the Qt Framework, so lots of things are changing. One of the new things is the addition of the QOpenGL* classes – the replacement for QGL* classes – was made available in Qt 5.

Note: QGL* classes are still usable through the Qt5OpenGL module, so old code relying on such classes will still work, but it is not recommended for new Qt5 OpenGL applications.

About Qt5 OpenGL Series

I’ve decided my old model of blog updating wasn’t working. I was basically putting it off until I understood “enough”. After understanding enough to make the blog post, I really wasn’t interested in making them any more. This is supposed to be at least a little fun and informative for me as well, or else it’s terribly boring. Because of the aforementioned reasons, my blog posts will usually cover what I’m working on, while I’m working on it (I should have changed to this model years ago).

As for these specific posts, all of my Qt5 OpenGL posts will assume you have basic knowledge of the Qt Framework. At least of signals and slots, and how to create a simple application from code. If you need clarification on anything, please leave a comment and I will respond. I will also assume at least a base understanding of OpenGL. Just be aware that this is also not an OpenGL tutorial OR a Qt tutorial, it’s a merging of the two technologies.

QOpenGLWindow

To start, we will be using the new QOpenGLWindow class.

QOpenGLWindow is a new class which inherits only from Qt5Gui-based classes. The reason this is important is it allows us to provide OpenGL abstractions without the need for the Qt5Widgets module. This was not previously the case in terms of QGL* functionality, which was tightly coupled with Qt Widgets.

The only things we really care about from QOpenGLWindow are the following functions:

  • initializeGL()
  • resizeGL(int width, int height)
  • paintGL()

As you can imagine, these functions simply allow us to perform our OpenGL logic. Really this is all we need to create a simple OpenGL application – much of the complicated setup of OpenGL is carried out by the Qt Framework.

QOpenGLFunctions

For convenience we can optionally subclass QOpenGLFunctions along with QOpenGLWindow. In modern versions of Qt, GLEW is not needed, and QOpenGLFunctions is a structure which will grant access to OpenGL ES 2.0 API. (We will be doing this briefly.)

You do not need to subclass to access the functionality of QOpenGLFunctions, if you’d rather, you may grab the available functions via QOpenGLContext. This may be instantiated as a local variable in one of two ways:

The reason this defaults to OpenGL ES 2.0 is for compatibility reasons, so we can easily port the product to embedded platforms (like Android and iOS). Our example will use this function context, but we can alternatively ask for specific function contexts. (eg. QOpenGLFunctions_4_3_Core)

This is nice because we can limit ourselves to only the context we want to support to. We have no immediate need for anything more than the OpenGL ES 2.0 API, so we will stick with the QOpenGLFunctions class.


 

Creating a Window

With this knowledge, we will start out by creating our OpenGL window. Thanks to Qt5, this application is pretty much trivial, since the hardest part of cross-platform OpenGL is setup. (Particularly concerning Windows because of the need for GLEW.) But it will provide a nice starting point for the rest of our projects.

1. Prepare a New Project (with Qt5.4 Gui Module)

For starters, we are going to create a new Qt Project, nothing special – I usually select Console Application and add what I need as I go.

The New Dialog Box.

The New Dialog Box.

The only thing we need to do is add the Qt5Gui module to our project, to do this we need to edit two lines of code in QMake <ProjectName>.pro file.

The original file looks like this:

And to include the Qt5Gui module, we need to change it to this:

Now our project is able to use the QOpenGL* classes, since they are a part of the Qt5Gui module.

2. Create a Window class

Next, we will need to create a Window class that uses the OpenGL classes we learned about previously. So just Ctrl+N, or File->New File or Project… and select “C++ Class” under the “C++” templates.

Creating a new class from template.

Creating a new class from template.

Class detail information.

Class detail information.

And then finally, we change the interface (window.h) as such:

Really there’s nothing new here. We’re just now utilizing the classes that I explained previously. The Window class inherits the functionality provided by QOpenGLWindow and QOpenGLFunctions, allowing us the functionality of OpenGL, and the capabilities of and OpenGL surface to draw to.

There are two new things that I have added, however.

  • teardownGL()
    • So we need a function to perform cleanup. Cleanup can be done in the destructor if QOpenGLWindow::makeCurrent() is called first, but I want to have a separate function that gets called to keep the interface clean, and obvious.
  • printContextInformation()
    • This helper function will print information about the OpenGL Context we have acquired, for debugging purposes.

Next we will apply the implementation (window.cpp):

This is all very basic setup, there’s really only one or two tricky parts.

  • initializeGL()
    • QOpenGLFunctions::initializeOpenGLFunctions(), which will perform initialization with the current OpenGL context that is present.
    • Window::printContextInformation() is our own function, that will print some version information in the console. (More on that later.)
    • glClearColor() is actually inherited from QOpenGLFunctions. As you may know, this simply sets the clear color.
  • resizeGL()
    • Right now, since we aren’t doing any complicated graphics, we simply do nothing in this function. We will see this become useful soon enough.
  • paintGL()
    • Again, very basic – we’re just clearing the background color with the color we set in initializeGL().
  • teardownGL()
    • Absolutely nothing, we are not allocating any resources on the GPU yet.
  • printContextInformation()
    • Okay, here is the meat of this tutorial. Based on how we create our OpenGL context, we can have access to different versions and functionality of OpenGL. It becomes useful (as a sanity check) to print the version to the best of our knowledge in the console.
    • Really, the only weird/tricky thing is that the QStrings will print to QDebug surrounded by quotation marks if we pass them on their own. Instead we claim to be qPrintable(), which is a simple Qt-controlled cast to char*, which will print without quotation marks.
    • Well, I was a little lazy on my cases for the switch-case, but I don’t consider that a fault here, because we’re undefining our laziness, and it generally isn’t too messy.
      • Note: For more complete Qt classes, ones which contain staticMetaObject, we can iterate over enumerations to form strings dynamically. QSurfaceFormat does not have meta associated with it, so we cannot here.

We’re almost done, there’s just one more part…

3. Update main.cpp to create a Window

There is actually one other Qt5 class that I failed to mention earlier; QGuiApplication. It’s not specific to OpenGL, so I wont bother to add a detailed description for it. Right now, the default functionality in your main.cpp involves creating a QCoreApplication, and running it.

We will be changing this out with a QGuiApplication (because that’s what we’re making). Before we exec(), anything though, we will create and show a Window.

This code is pretty self-explanatory. QSurfaceFormat is how we set the requested OpenGL Version. Providing a QSurfaceFormat isn’t explicitly needed, but it’s a good way to check the functionality of our printContextInformation() function.

At this point, you should be able to save, build, and run. And with the current input, you will see that the response you get (if your computer supports OpenGL 3.3) is:

OpenGL 3.3 ( CoreProfile )

and you should have a window that is pretty bland for all the work we did.

The final executable.

The final executable.

Play around with changing CoreProfile to CompatibilityProfile, and between different version numbers; check that the output makes sense. NoProfile is reserved for versions which did not include Core/Compatibility context requests.

Summary

That’s all for this tutorial, today we learned:

  • There is difference between Qt5Gui and Qt5OpenGL classes; for newer applications, favor Qt5Gui-based OpenGL classes.
  • We can now create an OpenGL window without the need for Qt5Widgets module.
  • Much better, more modern OpenGL support is in-tact for Qt5+ through the Qt5Gui module.
  • The basic setup to get an 800×600 OpenGL window running on your machine.

View Code on GitHub

Update (3/5/15)

As mentioned by A. Tabibi in the next tutorial, there is a problem with connecting QOpenGLContext’s aboutToBeDestroyed() signal with teardownGL() as a slot. The aboutToBeDestroyed() signal is emitted after the QObject has already disconnected it’s signal/slot pairs. Because of this, the slot never actually gets called. To avoid this behavior, we need to simply makeCurrent() and call the teardownGL() function in the destructor ourselves. I would have liked to avoid this, but it seems to not be an option at this time.

Cheers!


Leave a comment

Your email address will not be published. Required fields are marked *

23 thoughts on “Qt5 OpenGL Part 0: Creating a Window

  • Viktor Jansa

    Thank you for this tutorial! But can you explain, how to use the openGLWidget in Qt5.4 with QT Designer to build more complex windows? This would be great!!

    • Trent Post author

      Actually, in my recent experiments I’ve done just that! It’s fairly simple. Instead of inheriting from QOpenGLWindow, you have to use the QWidgets library to inherit from QOpenGLWidget. Then, from the designer you simply promote a regular QOpenGLWidget form item to a item.

      See my following commit from my test application for more information about this:
      https://github.com/TReed0803/QtOpenGL/blob/master/KarmaView/mainwindow.ui#L1710-L1716

      Feel free to git pull and take a look at the project files to see how I’m doing it. Keep in mind this is all very experimental so I don’t promise that I’m doing anything the _best_ way, just that it’s _a_ way of doing it.

      – Cheers!

      • Brian

        Great article! The disappointing part of all of this though is if you don’t want to be forced to render to a framebuffer object every frame, but do want Qt’s widgets in your interface, you’re, well, screwed. :( This is a pretty big fail on Qt’s part. There doesn’t appear to be any recourse other than using the deprecated QGLWidget, which is obviously a bad thing.

        • Trent Post author

          True, they could have allowed you to render directly to the screen using glViewport or something. Since the use case for Qt is general purpose GUI programming, I can see how they came to the decision of using a Framebuffer Objects. This decision was probably made without consideration for the needs of a high-end graphics pipeline. It shows in many aspects of their design, and unfortunately makes it a bad candidate for games and rich media.

          Case in point is the QOpenGLFramebufferObject class itself, which only allows for a single attachment via the interface. This class is so hilariously useless from a graphics programmer perspective that it might as well not have existed. The only use case I can think of is if you want to utilize the Qt classes to capture, copy, or save out the rendered information. A task which is just as easy to do using GL calls anyways. A more useful version of this class would be as I’ve redesigned it in the provided framework (see my OpenGLFramebufferObject implementation.) Because the primary use of a Framebuffer Object is that you can attach multiple outs, and render data to multiple textures (deferred rendering, anyone?), as well as query your Framebuffer Object to see if it’s valid. This is a case where they attempted to hide information to make things “easier”, and in doing so they crippled advanced users. (Ask the widget to create a depth or stencil buffer? Give me a break…)

          This is just an example of the amount of classes I’ve had to design myself because of what I felt was lack of proper design by the Qt team for high-performance graphics programming. On one side of the coin, I guess Qt was never meant for this kind of activity. Alternatively, I don’t see why it couldn’t have been designed in such a way. A simple class abstraction of OpenGL is all we really needed, and there is a lot of that available in modern Qt – QOpenGLContext, QOpenGLVertexBufferObject, and QOpenGLBuffers to name a few.

          The unfortunate truth of the situation though is the interface is already a part of Qt, and the interface is the problem. It cannot be “fixed”, it can only be maintained and will never really become better. If you need high-performance graphics and a killer UI system for a content creation tool – Qt might still be the king of this scenario. But if you want something that’s bleeding every ounce of capabilities out of OpenGL, you’re better off using a simpler window manager like SDL.

          • Brian

            Right, I think their desire to wrap all these OpenGL concepts is kind of dumb. Just give me a hook into my GL calls without any performance bottlenecks, and I’m happy. I mean, I’m glad they have a route for compositing, and such, but they make it sound like it’s just this awful thing to attempt to get a simple non-fbo backed GL window and that you should only do it “when absolutely necessary”. Fortunately, I did find a way to do this. I should’ve looked harder before commenting before.

            Anyway, QWidget::createWindowContainer() saves the day. You can create nice widget layouts within creator, and then create a widget after setupUi() that wraps your QOpenGLWindow and insert it into one of your layouts. I have read this may have problems on OS X, but it works great on Windows so far.

  • Kevin Wentzell

    Great intro! Just a few quick suggestions for neatness/sanity:

    You should use Q_DECL_OVERRIDE for overriding virtual protected functions, this way the compiler will throw a warning if the function was not defined in the inherited class. i.e. :
    void initializeGL() Q_DECL_OVERRIDE;

    You can use qDebug().noquote() to avoid having to use QPrintable(). This was introduced in Qt5 along with nospace() for easier stream output.

    You should explicitly define the constructor for Window and put the stuff from main in there. ie:

    Window::Window()
    {
    // initialize the GL context before the window is shown, otherwise we’ll end up with a Compatability Profile
    QSurfaceFormat format;
    format.setRenderableType( QSurfaceFormat::OpenGL ) ;
    format.setProfile( QSurfaceFormat::CoreProfile );
    format.setVersion( 3, 3 );

    setFormat( format );

    resize( 800, 600 );
    show();
    }

    I guess this is personal preference, but it’s a good idea to put relevant GL context declarations in Window.

    Cheers!

    • Trent Post author

      Thanks Kevin!

      I am still learning about a lot of Qt stuff, so this is very helpful to hear. :)

      As for explicitly defining the context in the constructor, I agree that is up to preference. I look at it as “configuring the Widget”, but you could see it as “configuring the context which is a dependent of the widget”. The way I structured future parts of the code allowed different surface configurations to be passed in (versions, profiles, etc).

      Honestly, I’d rather have deeper control over the creation of the context, but I figured this would do nicely for learning OpenGL development paired with Qt API. Personally, I’d say: if you are never going to change or configure the context otherwise, I do agree with putting it in the constructor. Otherwise, depending on the level of control you want to have in configuring the widget, either create some enum to pass in to have the ctor configure the surface, or configure the surface yourself for more control.

  • John Green

    For QT5,I thing you should use a simple qwindow and an openglcontext to do your own rendering,rather than any QOpenGLWindow or QOpenGLWidget.The QOpenGLXXX classes employ a quite weird rendering process,all of the renderings are drawn to the FBO with the ID 1,rather than directly to the framebuffer .And it also do a lot of extra OpenGL funtions behind ,which are totally beyond our control,such as glviewport,glBindFramebuffer,etc.All of these lead to a less efficient performance(25% for a simple scene),and likely a confused result of your own rendering.So If you know OpenGL pipleline well and want to make the rendering process more efficint and accurate,the QOpenGLXXX classed are definitely not good choices.

    • Trent Post author

      That’s a good point – so my intention was to use OpenGL within a widget, which would definitely require sticking with something similar to QOpenGL*; and I suppose I never mention that. The intent here isn’t to make a window suitable for running a game in, it’s to make a window suitable for placing a UI around.

      Unfortunately, I was a bit rushed at the end – so I didn’t ever get to actually make the UI tutorials (though I did post some example screen-shots of the UI I created for the final product here). But you could imagine that you have a QOpenGL it doesn’t take much work to turn it into a QOpenGLWidget, and then place it within a UI made with QtCreator. I probably should have structured my tutorials like this from the start, but wanted to kind of build up to teaching the reader that there is little difference between a QOpenGLWindow, and QOpenGLWidget.

      Honestly, if you just want a window to draw to – don’t use Qt. I just don’t see the point, it’s such a heavy dependency. Use something lighter like SDL2 or SFML. Not to mention the licensing is much more complicated for Qt, it’s only worth the headaches if you need the UI components in my mind. However, whatever gets your window drawn is A-OK in my book. ;)

      • John Green

        If you read the official examples well,you would know the method :
        “static QWidget * QWidget::createWindowContainer(QWindow *window, QWidget *parent=0, Qt::WindowFlags flags=0);”
        So, it is easy to generate a QWidget from a Qwindow,and of course we can totally take the whole advantage of the QT UI system.Meanwhile the benefits I talked about before could remain,such as hight performance and total control of the OpenGL rendering pipeline.
        The QOpenGLXX classes are encapsulated too much with so many bugs and it is kind of nonsense to forced us to take their weird way of rendering.The QOpenGLXX classes can neither help you to understand the window system well(such as hwnd, messages, openglcontext),nor can it lead you in advance of the OpenGL dvelopment(OpenGL API,OpenGL Pipeline ,latest extensions from glew/glee,etc).Up to now they are just some rendering toys for the rookies,and the rookies even don’t want to learn deeply.

        • Trent Post author

          Hmm, creating a custom OpenGL-enabled widget; I guess I hadn’t considered that as a possibility. I’ll have to give it another go when I start working with Qt again in the future – Thanks!

        • Appcypher

          Interesting. Only if there was a good tutorial on your method.
          I’ve always felt like Qt’s way of rendering through the framebuffer is a big overhead for graphics-intensive app.
          I was right. I’m unable to dial up the rendering and graphical features of my Animation app. The performance slows down considerably.

  • Duffy

    Thought I’d come back to this, just found these tutorials recently and they’ve been very helpful for someone who last used OpenGL with the fixed pipeline (yeah, I’m old).

    I don’t klnow whether this was the case at the time but it’s currently documented at http://doc.qt.io/qt-5/qopenglwidget.html that the destructor will need to take over all mopping up as ” the same cleanup steps must be performed from the derived class’ destructor, since the slot connected to the signal will not get invoked when the widget is being destroyed.”.

    There is a reason this design decision was made and the primary problem that could be cause is not directly invoked events could attempt to call methods that are no longer available or that invoked methods by this connection could cause a race condition whereby data or composed objects that the method relies on are destroyed before the call finishes.

    In short: it’s a bad idea to call methods on an object when its destructor has been called as chaos may ensue up to and including segmentation faults therefore all references to an object are removed to prevent the possiblity of an event fired from another thread attempting to access the object inappropriately.

    OK, that wasn’t so short but, well.

    Actualy, if you haven’t already read the description on the above link and you still have question why certain design decisions were made there’s quite a lot of info there (including the wrappers and framebuffer use, yeah). In all fairness, their aim is cross-platform compatibility (including Android/iOS) which is almost always the reason for performance compromises. Its what works for me, but you’re right that it’s not the best thing for game performance.

    • Trent Post author

      Hey Duffy,

      Glad you got some use out of these tutorials! :)
      And sadly, some new students are still being taught fixed-function pipelines, and many students are afraid to use OpenGL in favour of DirectX’s object-oriented API… I was trying to teach people with these articles that it’s not that bad, and if you really want the OOP can still be accomplished with lightweight wrapper classes. Hopefully I accomplished that.

    • Trent Post author

      We get into using depth in later tutorials (it’s a requirement for 3D objects with concave surfaces). If you really want to work with depth at this level, it’s not hard – just be sure to clear the depth buffer along with the colour buffer.

  • friv box

    This is very interesting, You’re an overly professional blogger.
    I have joined your rss feed and look ahead to in quest of more of
    your fantastic post. Additionally, I’ve shared your web site
    in my social networks

    • Trent Post author

      Thanks friv! :)
      I’ve been pretty checked-out of my blog lately. Work has been keeping me very, very busy. But I hope if I come back to this that future blog posts will be interesting, so thanks for subscribing.

  • WCK

    thanks you,i created the window followed your step,but i want to know how to import a 3D model into this window and display it.