📜 ⬆️ ⬇️

OpenGL in Qt 5.1 - Part 1 and 2

This article is a translation of an OpenGL in Qt 5.1 article - Part 1 and Part 2.


Part 1



This article is the first in the series. It will show how to use OpenGL in Qt 5 . This article will be a brief insight into the history of OpenGL support in Qt. Then we proceed to the description of the first part of the new features that are included in Qt 5.1 . Subsequent articles will describe more features, as well as some simple examples of how easy Qt can be used to work with OpenGL.


')

(Very) A brief history of Qt and OpenGL


Qt has a long history of drawing support with OpenGL. Most Qt developers have ideas about QGLWidget and, possibly, some OpenGL-based engines. This allows you to work with raw OpenGL or use the convenience provided by the QPainter API. In addition to this, Qt provides several useful wrappers around various OpenGL objects: QGLShaderProgram , QGLFramebufferObject , QGLBuffer , etc.

When designing Qt 5, these QGL * classes were labeled as “done” and the newest QOpenGL * classes became part of the QtGui module ( translator's note - it used to be QtOpenGL ). The reason for these changes is that the new rendering in Qt Quick 2 is based on OpenGL, and is currently the main part of the Qt graphics offerings. In addition, new QOpenGL * classes can be used as a direct replacement for old QGL * classes. It is now recommended to use the QOpenGL * classes from the QtGui library.

Qt 5.0 provides (basically) the same subset of OpenGL functionality that Qt 4.8 provides. This was required, among other things, for the functionality required in Qt Quick 2 . In addition to the functionality of Qt 4.8, Qt 5.0 provides the means to easily create your own OpenGL windows and contexts on any platform. You no longer have to mess around with the features of various platforms to create a context that can support the OpenGL Core profile . You can simply use QOpenGLContext and save yourself from gray hair!

In Qt 5.1, our adventures begin with a demonstration of more and more OpenGL functionality, so as to make using OpenGL and Qt simple, elegant and, I hope, fun! To this end, KDAB has invested heavily in expanding the boundaries of Qt and OpenGL.


Functions, functions everywhere!

Frankly speaking, working with OpenGL on some platforms can be difficult.
. One of the main reasons for these difficulties is the need to assign the address of the entry point dynamically, at runtime, and not at build time (when the linker is able to do this). For example, on Microsoft Windows, the address of any function entered in OpenGL since version 1.1 must be assigned at run time. That is, it must be done for almost all the functions used in modern OpenGL!

To solve these problems, Qt provides a couple of useful utilities: QOpenGLContext :: GetProcAddress () and QOpenGLFunctions . The former can be used to manually assign entry points, while the latter is a class whose methods are a display of a common subset of the functions OpenGL 2 and OpenGL ES 2 . These helpers are as good as possible. The problem is that QOpenGLFunctions is limited in the methods provided (subsets of OpenGL 2 and OpenGL ES 2). And manual assignment of entry points is very tedious work, fraught with errors. Alternatively, you can use functions from third-party libraries that solve these problems, such as Glew or GLee . True, these solutions also cause problems for obtaining the required functionality and convenient work with Qt (for example, taking into account the order of the readers).

Use QOpenGLContext :: versionFunctions () ! This is a modest small function - your explorer into the “utopia” of addresses (entry points) of OpenGL functions :) This function can be used to get a pointer to an object (with methods for each function) in the required version and OpenGL profile. Let's take a look at a simple example. Let's say we have a subclass of QWindow that renders. We want to create an OpenGL 4.3 Core profile and use all the features provided. It's very easy to do this:

Window::Window(QScreen * screen) : QWindow(screen) { //  Qt    OpenGL    setSurfaceType(OpenGLSurface); //     -  QSurfaceFormat format; format.setDepthBufferSize(24); format.setMajorVersion(4); format.setMinorVersion(3); format.setSamples(4); format.setProfile(QSurfaceFormat::CoreProfile); setFormat(format); create(); //  OpenGL  m_context = new QOpenGLContext; m_context->setFormat(format); m_context->create(); //       m_context->makeCurrent(this); //         // m_funcs  : 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 now on, we can simply use the member functions of the QOpenGLFunctions_4_3_Core object. For example:
  //  Vertex Attrib Divisor //    instanced rendering // (  OpenGL 3.3) m_funcs->glVertexAttribDivisor(pointLocation, 1); //    compute shader // (  OpenGL 4.3) m_funcs->glDispatchCompute(512 / 16, 512 / 16, 1); 




As we can see, it is easy enough to have all the OpenGL functionality in your hands for any platform that supports it. In addition, QOpenGLContext , QOpenGLFunctions_4_3_Core and similar classes minimize the work to use functions by distributing backends containing valid function pointers. Also, this approach automatically takes care of platform-specific addresses of functions (for example, when using multiple threads and contexts or several GPUs). The code for these classes is generated automatically using an auxiliary utility, so it is very easy to update it when a new version of OpenGL is released.


OpenGL Extensions (OpenGL Extensions)


OpenGL has a popular extension mechanism that allows vendors to introduce new or experimental functionality and APIs to test whether they are useful and well thought out. Unfortunately, if the extension introduces new functions, then they also need to specify the address as for other OpenGL functions (as indicated above).

To use OpenGL extensions, you need to go through two stages. Qt helps with both stages:

Stage 1:

Verify that the current implementation supports the required extension. If the extension introduces a new API, specify the entry point. To verify that the extension is supported, you need to use the QOpenGLContext :: hasExtension () method. In addition, you can use OpenGLContext :: extensions () to get the full list of supported extensions:

 //   QList extensions = m_context->extensions().toList(); std::sort(extensions); qDebug() << "Supported extensions (" << extensions.count() <<")"; foreach (const QByteArray &extension, extensions) { qDebug() << " " << extension; } 


Stage 2:

At the second stage, we would need to use our old friend - the QOpenGLContext :: GetProcAddress () method. In Qt 5.1, the QtOpenGLExtensions module is responsible for this. This module is a static library and contains a class for each OpenGL extension (which introduces a new API) from the Khronos registry . To use the OpenGL extension, use code similar to the following:

  //    if (!m_context->hasExtension(QByteArrayLiteral("GL_ARB_instanced_arrays")) { qFatal("GL_ARB_instanced_arrays is not supported"); } //          QOpenGLExtension_ARB_instanced_arrays * m_instanceFuncs = new QOpenGLExtension_ARB_instanced_arrays(); m_instanceFuncs->initializeOpenGLFunctions(); //    m_instanceFuncs->glVertexAttribDivisorARB(pointLocation, 1); 


As with the basic functions of OpenGL, the code for the extensions is generated and, therefore, easily updated in the future.


Part 2



Vertex Array Objects


Qt has a QOpenGLBuffer (previously QGLBuffer ) to help manage various types of OpenGL buffer objects, such as per-vertex attribute data and element index buffers . OpenGL also has a special container, the type of which is called the Objects Vertex Array (VAOs) , which helps in working with the vertex buffer object (VBO) sets.

KDAB has added code for Qt 5.1 that encapsulates several VAOs with the QOpenGLVertexArrayObject class. Linking an instance of this class tells OpenGL to "remember" any vertex specification state that you would like to set later. We can later restore the required state specifications very quickly by simply re-linking the VAO itself. This allows us to very quickly switch between the vertex states for the “objects” that we want to draw in our rendering function:
Translator's Note
In my opinion, instead of “binding,” it would be more appropriate to use the “Russified” verb “bandage”, which may be more common for developers to hear.


 void Scene::initialize() { //      QOpenGLContext  // m_shaderProgram  QOpenGLShaderProgram //  VAO     m_vao1 = new QOpenGLVertexArrayObject( this ); m_vao1->create(); m_vao1->bind(); //   VBO  IBO ( QOpenGLBuffer   , //  ,    ..).   "" //    VAO m_positionBuffer.create(); m_positionBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); m_positionBuffer.bind(); m_positionBuffer.allocate(positionData, vertexCount * 3 * sizeof(float)); m_shaderProgram.enableAttributeArray("vertexPosition"); m_shaderProgram.setAttributeBuffer ("vertexPosition", GL_FLOAT, 0, 3); m_colorBuffer.create(); m_colorBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); m_colorBuffer.bind(); m_colorBuffer.allocate(colorData, vertexCount * 3 * sizeof(float)); m_shaderProgram.enableAttributeArray("vertexColor"); m_shaderProgram.setAttributeBuffer ("vertexColor", GL_FLOAT, 0, 3); //    ,  , // , ... ... //  VAO     m_vao2 = new QOpenGLVertexArrayObject(this); m_vao2->create(); m_vao2->bind(); //   VBO  IBO    ... // "  "    (.  ) m_skyBoxVAO = new QOpenGLVertexArrayObject(this); ... } void Scene::render() { //   m_funcs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //       m_phongShaderProgram->bind(); ... //           m_vao1->bind(); m_funcs->glDrawElements(...); //           m_vao2->bind(); m_funcs->glDrawElements(...); //    /   .. //     m_skyboxShaderProgram->bind(); ... m_skyboxVAO->bind(); m_funcs->glDrawElements(...); ... } 


Translator's Note
“Rinse and repeat” is a sarcastic metaphor indicating the repetition of instructions. It has roots in the phrase “wash, rinse, repeat” , which is indicated on the packaging of most brands of shampoos.


VAOs were introduced with the OpenGL 3 version, but they are required for OpenGL version over 3.1 with Core Profile. In addition, VAO are available as GL_ARB_vertex_array_object or GL_OES_vertex_array_object extensions in OpenGL 2 and OpenGL ES 2, respectively. The QOpenGLVertexArrayObject class will use the main functionality (if possible) or access the extension (if any) if necessary.

Using VAO can significantly simplify the visualization code and increase performance, because the OpenGL driver will do less validation checks (than if it were doing more buffer operations).

Part 3,4,5 coming soon ... PS: A big request, all stylistic and grammatical errors, as well as inaccuracies of the translation should be reported through drugs. All will rule as they become available.

Source: https://habr.com/ru/post/206156/


All Articles