Location>code7788 >text

Start from Scrat: Guide to Drawing with OpenGL in Qt

Popularity:95 ℃/2025-04-05 22:58:15

This article only introduces the use of basic QOpenGLWidget and QOpenGLFunctions. Friends who want to learn OpenGL are recommended to visit the classic OpenGL learning website:LearnOpenGL CN

In this article, we will take drawing a classic triangle as an example and talk about how to use OpenGL in Qt to draw GPU.

Preface

In high-performance rendering scenarios, CPU resources are often over-consuming, resulting in interface stuttering. As an industry-standard graphics API, OpenGL can significantly reduce CPU load through GPU hardware acceleration. This article will use drawing triangles as an example to teach you how to implement cross-platform GPU rendering through QT's QOpenGLWidget and QOpenGLFunctions.

QOpenGLFunctions

There are differences in the implementation of OpenGL functions on different platforms (Windows/Linux/Mac). For example:

platform Function loading method
Windows wglGetProcAddress
Linux glXGetProcAddress

Qt byQOpenGLFunctionsEncapsulated these underlying differences, developers only need to inherit this class and use itglClear()The unified interface calls OpenGL functions without writing platform-specific code. In this way, we can use an OpenGL phase on different platforms using a set of code. It is also very simple to use this class, let our class inherit directlyQOpenGLFuntionsThat's fine. It can also cooperateQOpenGLWidgetCome to use, ininitializeGLIn the function, callinitializeOpenGLFunctionsAfter that, it's OKDirectly use OpenGL functions

Loading under Windows (wglGetProcAddress

For example, under Windows, we usewglGetProcAddressTo load these functions dynamically (e.g.glClear), here is the loading code:

  • Contains the necessary header files

    #include <>
     #include <GL/>
     #include <GL// Provide OpenGL extension statement
  • Define function pointer type

    // Example: Define the function pointer type of glClear
     typedef void (APIENTRY *PFNGLCLEARPROC)(GLbitfield);
     PFNGLCLEARPROC glClear;
  • Loading OpenGL functions

    // Initialize OpenGL function
     void initOpenGLFunctions() {
         // 1. Load the OpenGL 1.1 function (provided by)
         glClear = (PFNGLCLEARPROC)wglGetProcAddress("glClear");
    
         // 2. Check whether the loading is successful
         if (!glClear) {
             // If it fails, it may be that the driver does not support the function
             MessageBoxA(NULL, "Failed to load glClear", "Error", MB_OK);
             exit(1);
         }
    
         // 3. Load other functions in a similar way...
         // glDrawArrays = (PFNGLDRAWARRAYSPROC)wglGetProcAddress("glDrawArrays");
         // ...
     }
  • Using loaded functions

    glClear(GL_COLOR_BUFFER_BIT); // It can be called normally now

Loading under Linux (glXGetProcAddress

And in linux, the loaded function becomes:glXGetProcAddress, the corresponding code is:

  • Contains the necessary header files

    #include <GL/>
     #include <GL// OpenGL extension for X11
     #include <GL/>
  • Define function pointer type

    // Example: Define the function pointer type of glClear
     typedef void (*PFNGLCLEARPROC)(GLbitfield);
     PFNGLCLEARPROC glClear;
  • Loading OpenGL functions

    void initOpenGLFunctions() {
         // 1. Load glClear
         glClear = (PFNGLCLEARPROC)glXGetProcAddress((const GLubyte*)"glClear");
        
         // 2. Check whether the loading is successful
         if (!glClear) {
             fprintf(stderr, "Failed to load glClear\n");
             exit(1);
         }
        
         // 3. Load other functions in a similar way...
         // glDrawArrays = (PFNGLDRAWARRAYSPROC)glXGetProcAddress((const GLubyte*)"glDrawArrays");
         // ...
     }
  • Using loaded functions

    glClear(GL_COLOR_BUFFER_BIT); // It can be called normally now

QOpenGLWidget

QOpenGLWidgetis a widget class provided by Qt to embed OpenGL rendering content in Qt applications. It inherits fromQWidget, an OpenGL context is managed internally (for example, under Windows callswglMakeCurrent / wglDoneCurrent) and frame buffers and provide the ability to seamlessly integrate with the Qt windowing system.For details, please see:QOpenGLWidget Class

We can create our own window, inherit QOpenGLWidget, and then rewrite the following three functions to handle some OpenGL-related work.

initializeGL

Initialize some OpenGL-related resources or states.This function is called firstresizeGLorpaintGLCalled before.

paintGL

Rendering OpenGL scenes, similar to what we usually useQWidget::paintEvent, called when the window needs to be updated.

resizeGL

Resize or project the OpenGL Viewport, etc., and call it when the window needs to be resized.

Complete code

#pragma once

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

#include ""

class COpenGLRenderWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    explicit COpenGLRenderWidget(QWidget *parent = nullptr);
    ~COpenGLRenderWidget() override;

private:
    void InitShaders();

private:
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int w, int h) override;
    
private:
    QOpenGLShaderProgram m_shaderProgram;
    QOpenGLBuffer m_vbo;
};

#include ""

 static const GLfloat coordinateBasic[] = {
     // Vertex coordinates, storing 3 xyz coordinates
     // x y z
     -0.5f, -0.5f, 0.0f,
      0.5f, -0.5f, 0.0f,
      0.0f, 0.5f, 0.0f,
 };

 constexpr auto VERTEX_SHADER_BASIC = R"(
 attribute vec3 vertexIn;
 varying vec2 textureOut;

 void main(void)
 {
     gl_Position = vec4(vertexIn, 1.0);
 }
 )";

 constexpr auto FRAGMENT_SHADER_BASIC = R"(
 varying vec2 textureOut;

 void main(void)
 {
     gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
 }
 )";

 COpenGLRenderWidget::COpenGLRenderWidget(QWidget *parent)
     : QOpenGLWidget(parent)
 {}

 COpenGLRenderWidget::~COpenGLRenderWidget()
 {}

 void COpenGLRenderWidget::initializeGL()
 {
     initializeOpenGLFunctions();
     glDisable(GL_DEPTH_TEST);

     m_vbo.create();
     m_vbo.bind();
     m_vbo.allocate(coordinateBasic, sizeof(coordinateBasic));

     InitShaders();

     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
     glClear(GL_COLOR_BUFFER_BIT);
 }

 void COpenGLRenderWidget::paintGL()
 {
     m_shaderProgram.bind();

     glDrawArrays(GL_TRIANGLES, 0, 3);

     m_shaderProgram.release();
 }

 void COpenGLRenderWidget::resizeGL(int w, int h)
 {
     glViewport(0, 0, w, h);
     update();
 }

 void COpenGLRenderWidget::InitShaders()
 {
     QOpenGLShader vertexShader(QOpenGLShader::Vertex);
     if (!(VERTEX_SHADER_BASIC))
     {
         qDebug() << "Vertex shader compilation failed. Error: " << ();
         return;
     }

     QOpenGLShader fragmentShader(QOpenGLShader::Fragment);
     if (!(FRAGMENT_SHADER_BASIC))
     {
         qDebug() << "Fragment shader compilation failed. Error: " << ();
         return;
     }

     m_shaderProgram.addShader(&vertexShader);
     m_shaderProgram.addShader(&fragmentShader);

     m_shaderProgram.link();
     m_shaderProgram.bind();

     m_shaderProgram.setAttributeBuffer("vertexIn", GL_FLOAT, 0, 3, 3 * sizeof(float));
     m_shaderProgram.enableAttributeArray("vertexIn");
 }