原文见;
http://www.kdab.com/opengl-in-qt-5-1-part-1/?utm_source=rss&utm_medium=rss&utm_campaign=opengl-in-qt-5-1-part-1 OpenGL in Qt 5.1 – Part 1 Published: March 15, 2013 by Sean Harmer
This blog is the first in a series that will show how to use OpenGLwith Qt 5. In this article, we shall take a very quick look at Qt’shistorical support for OpenGL and then go on to describe the first batchof new features coming in Qt 5.1 that will enhance Qt’s OpenGL support.Upcoming blogs in this series will describe more features and show somesimple examples of how easy it is to use Qt for OpenGL development.
A (very) brief history of Qt and OpenGL Qt has a long history of supporting drawing with OpenGL. Most Qt developers are aware of
QGLWidgetand maybe the various incarnations of the OpenGL-based paint engines.These allow drawing with raw OpenGL or with the convenience of theQPainter API respectively. In addition to these, Qt also offers somehelpful wrappers around other OpenGL object types such as
QGLShaderProgram,
QGLFramebufferObject,
QGLBuffer etc.
During the design of Qt 5, these QGL* classes were marked as "Done"and shiny new QOpenGL* replacements were introduced and placed directlyinto the QtGui library. The reason for these changes is that the new QtQuick 2 renderer is based upon OpenGL and so is now a core part of Qt’sgraphical offerings. Also, the new QOpenGL* classes can be used asdirect replacements for the older QGL* classes. For new code, theQOpenGL* classes from QtGui are recommended.
Qt 5.0 exposed basically the same subset of OpenGL functionality asQt 4.8 did, which is pretty much the intersection of OpenGL 2 and OpenGLES 2. This also happens to be the functionality needed by Qt Quick 2.In addition to the Qt 4.8 functionality, Qt 5.0 also makes it very easyto create native
windows and OpenGL contexts on any platform. No moremessing around with the idiosyncrasies of various platforms to create acontext that can support the OpenGL Core profile. Just use
QOpenGLContext and save yourself from some grey hairs!
With Qt 5.1, we are beginning the adventure of exposing more and moreOpenGL functionality so as to make using OpenGL with Qt simple, elegantand hopefully fun! To this end, KDAB has invested significant resourcesinto pushing the boundaries with Qt and OpenGL.
Functions, functions everywhere! OpenGL is, to put it bluntly, a bit of a pain to work with on someplatforms. One of the major reasons for this pain is the need to resolveentry point addresses dynamically at runtime rather than the build timelinker being able to do so. For example, on Microsoft Windows, theaddress of any function introduced since OpenGL 1.1 must be resolved atrun-time. That is nearly all of functions used in a modern OpenGLapplication!
To assist with this Qt has provided a couple of helpful utilities:
QOpenGLContext::getProcAddress() and
QOpenGLFunctions.The former can be used to perform manual resolution of entry points,whilst the latter is a class that has member functions mapping to thecommon subset of functions in OpenGL 2 and OpenGL ES 2. These helpersare great, as far as they go. The issues are that QOpenGLFunctions islimited in what it exposes (the above mentioned subset of OpenGL 2 andES 2) and manual resolving of entry points is extremely tedious anderror-prone. Alternatively it is possible to use an external functionresolver such as
GLEW or
GLee. However, it is often a pain to get these to play nicely with Qt in termsof header inclusion order etc.
Enter
QOpenGLContext::versionFunctions()! This unassuming little function is your gateway to OpenGL entry point utopia
This function can be used to obtain a pointer to an object with memberfunctions for every function in the requested OpenGL version andprofile. Let’s take a look at a simple example. Say we have created a
QWindow sub-class on which to render and we now wish to create an OpenGL 4.3
Core profile context and resolve every function. To do so is as simple as:
- Window::Window( QScreen* screen )
- : QWindow( screen ),
- {
- // Tell Qt we will use OpenGL for this window
- setSurfaceType( OpenGLSurface );
-
- // Specify the format and create platform-specific surface
- QSurfaceFormat format;
- format.setDepthBufferSize( 24 );
- format.setMajorVersion( 4 );
- format.setMinorVersion( 3 );
- format.setSamples( 4 );
- format.setProfile( QSurfaceFormat::CoreProfile );
- setFormat( format );
- create();
-
- // Create an OpenGL context
- m_context = new QOpenGLContext;
- m_context->setFormat( format );
- m_context->create();
-
- // Make the context current on this window
- m_context->makeCurrent( this );
-
- // Obtain a functions object and resolve all entry points
- // m_funcs is declared as: QOpenGLFunctions_4_3_Core* m_funcs
- m_funcs = m_context->versionFunctions();
- if ( !m_funcs ) {
- qWarning( "Could not obtain OpenGL versions object" );
- exit( 1 );
- }
- m_funcs->initializeOpenGLFunctions();
- }
From that point on we can simply use member functions on the
QOpenGLFunctions_4_3_Core object. For example:
- // Set a vertex attribute divisor
- // Used with instanced rendering
- // Introduced in OpenGL 3.3
- m_funcs->glVertexAttribDivisor( pointLocation, 1 );
-
- // Dispatch processing via a compute shader
- // Introduced in OpenGL 4.3
- m_funcs->glDispatchCompute( 512 / 16, 512 / 16, 1 );
As you can see, this easily puts all OpenGL functionality into your hands on any platform that supports it. Moreover, the
QOpenGLContext,
QOpenGLFunctions_4_3 and
similar classestry hard to minimise the amout of work done in resolving functions bysharing backends containing the actual function pointers. In addition,this approach automatically takes care of context-specific functionaddresses (e.g. if using multiple threads and contexts or multipleGPUs). The code for these classes is generated automatically by a helperutility so it is easy to update as new OpenGL versions are released.
OpenGL Extensions OpenGL also has a well-known extension mechanism that allows vendorsto introduce new or experimental functionality and API to see if it isuseful and well thought out. Unfortunately, if an extension introducesnew functions then these too need to be resolved as for other OpenGLfunctions as described above.
There are two stages to being able to use an OpenGL extension:
- Check if the implementation supports the extension
- If the extension introduces new API, resolve the entry points
Qt helps with both of these steps. To check if an extension is supported we can use
QOpenGLContext::hasExtension(). Alternatively to get the complete list of supported extensions use
OpenGLContext::extensions():
- // Query extensions
- QList<QByteArray> extensions = m_context->extensions().toList();
- std::sort( extensions );
- qDebug() << "Supported extensions (" << extensions.count() << ")";
- foreach ( const QByteArray &extension, extensions )
- qDebug() << " " << extension;
For the second part, we would need to use our old friend
QOpenGLContext::getProcAddress(). New in Qt 5.1 though is the module
QtOpenGLExtensions. This module builds as a static library and contains a class for every OpenGL extension in the
Khronos registry that introduces new API. To use an OpenGL extension we can therefore use code similar to this:
- // Check if extension is supported
- if ( !m_context->hasExtension( QByteArrayLiteral(
- "GL_ARB_instanced_arrays" ) )
- qFatal( "GL_ARB_instanced_arrays is not supported" );
-
- // Create instance of helper class and resolve functions
- QOpenGLExtension_ARB_instanced_arrays* m_instanceFuncs =
- new QOpenGLExtension_ARB_instanced_arrays();
- m_instanceFuncs->initializeOpenGLFunctions();
-
- // Call an extension function
- m_instanceFuncs->glVertexAttribDivisorARB( pointLocation, 1 );
As with the core OpenGL functions, the code for the extensions is generated for easy updates in the future.
Watch this space for the next installment…