找回密码
 立即注册
首页 业界区 业界 从零开始:在Qt中使用OpenGL绘制指南

从零开始:在Qt中使用OpenGL绘制指南

段干叶农 2025-6-2 22:57:15
本文只介绍基本的 QOpenGLWidget 和 QOpenGLFunctions 的使用,想要学习 OpenGL 的朋友,建议访问经典 OpenGL 学习网站:LearnOpenGL CN
本篇文章,我们将以绘制一个经典的三角形为例,讲一讲,怎么在 Qt 中使用 OpenGL 来进行 GPU 绘制。
前言

在高性能渲染场景中,CPU资源常被过度消耗,导致界面卡顿。而OpenGL作为业界标准的图形API,能通过GPU硬件加速显著降低CPU负载。本文将以绘制三角形为例,教你如何通过Qt的QOpenGLWidget和QOpenGLFunctions实现跨平台GPU渲染。
QOpenGLFunctions

OpenGL函数在不同平台(Windows/Linux/Mac)的实现存在差异。例如:
平台函数加载方式WindowswglGetProcAddressLinuxglXGetProcAddressQt通过QOpenGLFunctions封装了这些底层差异,开发者只需继承此类,即可用glClear() 等统一接口调用OpenGL函数,无需编写平台特定代码。通过这样,我们就可以用一套代码,在不同平台下使用 OpenGL 相。要使用这个类也很简单,让我们的类直接继承 QOpenGLFuntions 就好了。同时也可以配合 QOpenGLWidget 来使用,在 initializeGL 函数里,调用 initializeOpenGLFunctions 后,就可以直接使用 OpenGL 的函数
Windows 下加载(wglGetProcAddress)

例如在 Windows 下,我们使用 wglGetProcAddress来动态加载这些函数(例如 glClear),下面是加载代码:

  • 包含必要的头文件
    1. #include <windows.h>
    2. #include <GL/gl.h>
    3. #include <GL/glext.h>  // 提供 OpenGL 扩展声明
    复制代码
  • 定义函数指针类型
    1. // 示例:定义 glClear 的函数指针类型
    2. typedef void (APIENTRY *PFNGLCLEARPROC)(GLbitfield);
    3. PFNGLCLEARPROC glClear;
    复制代码
  • 加载 OpenGL 函数
    1. // 初始化 OpenGL 函数
    2. void initOpenGLFunctions() {
    3.     // 1. 加载 OpenGL 1.1 函数(由 opengl32.dll 提供)
    4.     glClear = (PFNGLCLEARPROC)wglGetProcAddress("glClear");
    5.     // 2. 检查是否加载成功
    6.     if (!glClear) {
    7.         // 如果失败,可能是驱动不支持该函数
    8.         MessageBoxA(NULL, "Failed to load glClear", "Error", MB_OK);
    9.         exit(1);
    10.     }
    11.     // 3. 类似方式加载其他函数...
    12.     // glDrawArrays = (PFNGLDRAWARRAYSPROC)wglGetProcAddress("glDrawArrays");
    13.     // ...
    14. }
    复制代码
  • 使用加载的函数
    1. glClear(GL_COLOR_BUFFER_BIT);  // 现在可以正常调用
    复制代码
Linux 下加载(glXGetProcAddress )

而在 linux 下,加载的函数变成了:glXGetProcAddress ,对应的代码是:

  • 包含必要的头文件
    1. #include <GL/gl.h>
    2. #include <GL/glx.h>  // X11 的 OpenGL 扩展
    3. #include <GL/glext.h>
    复制代码
  • 定义函数指针类型
    1. // 示例:定义 glClear 的函数指针类型
    2. typedef void (*PFNGLCLEARPROC)(GLbitfield);
    3. PFNGLCLEARPROC glClear;
    复制代码
  • 加载 OpenGL 函数
    1. void initOpenGLFunctions() {
    2.     // 1. 加载 glClear
    3.     glClear = (PFNGLCLEARPROC)glXGetProcAddress((const GLubyte*)"glClear");
    4.    
    5.     // 2. 检查是否加载成功
    6.     if (!glClear) {
    7.         fprintf(stderr, "Failed to load glClear\n");
    8.         exit(1);
    9.     }
    10.    
    11.     // 3. 类似方式加载其他函数...
    12.     // glDrawArrays = (PFNGLDRAWARRAYSPROC)glXGetProcAddress((const GLubyte*)"glDrawArrays");
    13.     // ...
    14. }
    复制代码
  • 使用加载的函数
    1. glClear(GL_COLOR_BUFFER_BIT);  // 现在可以正常调用
    复制代码
QOpenGLWidget

QOpenGLWidget 是 Qt 提供的一个 widget 类,用于在 Qt 应用程序中嵌入 OpenGL 渲染内容。它继承自 QWidget,内部管理了一个 OpenGL 上下文(例如 windows 下调用 wglMakeCurrent / wglDoneCurrent)和帧缓冲区,并提供了与 Qt 窗口系统无缝集成的能力。详细内容可看:QOpenGLWidget Class
我们可以创建自己的窗口,并继承 QOpenGLWidget,然后重写下面三个函数,来处理一些 OpenGL 相关的工作。
initializeGL

初始化一些 OpenGL 相关的资源或者状态。这个函数在在第一次调用 resizeGL或者 paintGL之前被调用。
paintGL

渲染 OpenGL 的场景,类似于我们平常使用的 QWidget::paintEvent,在窗口需要更新时调用。
resizeGL

调整 OpenGL Viewport 的大小或者投影等,在窗口需要调整大小时调用。
完整代码
  1. #pragma once
  2. #include <QOpenGLBuffer>
  3. #include <QOpenGLWidget>
  4. #include <QOpenGLShaderProgram>
  5. #include <QOpenGLFunctions>
  6. #include "FrameObserver.h"
  7. class COpenGLRenderWidget : public QOpenGLWidget, protected QOpenGLFunctions
  8. {
  9.     Q_OBJECT
  10. public:
  11.     explicit COpenGLRenderWidget(QWidget *parent = nullptr);
  12.     ~COpenGLRenderWidget() override;
  13. private:
  14.     void InitShaders();
  15. private:
  16.     void initializeGL() override;
  17.     void paintGL() override;
  18.     void resizeGL(int w, int h) override;
  19.    
  20. private:
  21.     QOpenGLShaderProgram m_shaderProgram;
  22.     QOpenGLBuffer m_vbo;
  23. };
复制代码
[code]#include "OpenGLRenderWidget.h"static const GLfloat coordinateBasic[] = {    // 顶点坐标,存储3个xyz坐标    // 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 (!vertexShader.compileSourceCode(VERTEX_SHADER_BASIC))    {        qDebug()

相关推荐

您需要登录后才可以回帖 登录 | 立即注册