在Qt中使用OpenGL绘制三角形指南

 更新时间:2025年04月07日 08:48:39   作者:师从名剑山  
在高性能渲染场景中,CPU资源常被过度消耗,导致界面卡顿,而OpenGL作为业界标准的图形API,能通过GPU硬件加速显著降低CPU负载,本文将以绘制三角形为例,教你如何通过Qt的QOpenGLWidget和QOpenGLFunctions实现跨平台GPU渲染,感兴趣的朋友一起看看吧

本文只介绍基本的 QOpenGLWidget 和 QOpenGLFunctions 的使用,想要学习 OpenGL 的朋友,建议访问经典 OpenGL 学习网站:LearnOpenGL CN

本篇文章,我们将以绘制一个经典的三角形为例,讲一讲,怎么在 Qt 中使用 OpenGL 来进行 GPU 绘制。

前言

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

QOpenGLFunctions

OpenGL函数在不同平台(Windows/Linux/Mac)的实现存在差异。例如:

平台函数加载方式
WindowswglGetProcAddress
LinuxglXGetProcAddress

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

Windows 下加载(wglGetProcAddress)

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

包含必要的头文件

#include <windows.h>
#include <GL/gl.h>
#include <GL/glext.h>  // 提供 OpenGL 扩展声明

定义函数指针类型

// 示例:定义 glClear 的函数指针类型
typedef void (APIENTRY *PFNGLCLEARPROC)(GLbitfield);
PFNGLCLEARPROC glClear;

加载 OpenGL 函数

// 初始化 OpenGL 函数
void initOpenGLFunctions() {
    // 1. 加载 OpenGL 1.1 函数(由 opengl32.dll 提供)
    glClear = (PFNGLCLEARPROC)wglGetProcAddress("glClear");
    // 2. 检查是否加载成功
    if (!glClear) {
        // 如果失败,可能是驱动不支持该函数
        MessageBoxA(NULL, "Failed to load glClear", "Error", MB_OK);
        exit(1);
    }
    // 3. 类似方式加载其他函数...
    // glDrawArrays = (PFNGLDRAWARRAYSPROC)wglGetProcAddress("glDrawArrays");
    // ...
}

使用加载的函数

glClear(GL_COLOR_BUFFER_BIT);  // 现在可以正常调用

Linux 下加载(glXGetProcAddress )

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

包含必要的头文件

#include <GL/gl.h>
#include <GL/glx.h>  // X11 的 OpenGL 扩展
#include <GL/glext.h>

定义函数指针类型

// 示例:定义 glClear 的函数指针类型
typedef void (*PFNGLCLEARPROC)(GLbitfield);
PFNGLCLEARPROC glClear;

加载 OpenGL 函数

void initOpenGLFunctions() {
    // 1. 加载 glClear
    glClear = (PFNGLCLEARPROC)glXGetProcAddress((const GLubyte*)"glClear");
    // 2. 检查是否加载成功
    if (!glClear) {
        fprintf(stderr, "Failed to load glClear\n");
        exit(1);
    }
    // 3. 类似方式加载其他函数...
    // glDrawArrays = (PFNGLDRAWARRAYSPROC)glXGetProcAddress((const GLubyte*)"glDrawArrays");
    // ...
}

使用加载的函数

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 的大小或者投影等,在窗口需要调整大小时调用。

完整代码

#pragma once
#include <QOpenGLBuffer>
#include <QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include "FrameObserver.h"
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 "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() << "Vertex shader compilation failed. Error: " << vertexShader.log();
        return;
    }
    QOpenGLShader fragmentShader(QOpenGLShader::Fragment);
    if (!fragmentShader.compileSourceCode(FRAGMENT_SHADER_BASIC))
    {
        qDebug() << "Fragment shader compilation failed. Error: " << fragmentShader.log();
        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");
}

到此这篇关于在Qt中使用OpenGL绘制指南的文章就介绍到这了,更多相关Qt使用OpenGL绘制指南内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言 哈希查找详解(哈希表的创建、处理冲突、查找等)

    C语言 哈希查找详解(哈希表的创建、处理冲突、查找等)

    哈希表是一种非常重要的数据结构,并在大量的计算机科学和工程应用中发挥重要作用,了解哈希表的原理和实现方式,将有助于我们更好地理解这个数据结构及如何应用它来解决实际问题,这篇文章主要介绍了C语言 哈希查找(哈希表的创建、处理冲突、查找等),需要的朋友可以参考下
    2024-01-01
  • C语言动态内存泄露常见问题内存分配改进方法详解

    C语言动态内存泄露常见问题内存分配改进方法详解

    今天遇见了一道有意思的内存泄露题目,特地分享给大家,相信屏幕前的你学习完一定有所收获,预祝读者学习愉快,多多进步早日升职加薪
    2021-10-10
  • C++实现基于静态数组的顺序表

    C++实现基于静态数组的顺序表

    这篇文章主要介绍了C++实现基于静态数组的顺序表,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-05-05
  • 浅谈Qt中使用CEF的几个要点(Windows下)

    浅谈Qt中使用CEF的几个要点(Windows下)

    下面小编就为大家带来一篇浅谈Qt中使用CEF的几个要点(Windows下)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • C语言中fgetgrent()函数和fgetpwent()函数的用法对比

    C语言中fgetgrent()函数和fgetpwent()函数的用法对比

    这篇文章主要介绍了C语言中fgetgrent()函数和fgetpwent()函数的用法对比,分别用于读取组格式函数和读取密码格式,需要的朋友可以参考下
    2015-08-08
  • C++ Opencv实现录制九宫格视频

    C++ Opencv实现录制九宫格视频

    这篇文章主要为大家介绍了如何利用C++和OpenCV库实现录制九宫格视频,文中的示例代码讲解详细,对我们学习OpenCV有一定帮助,感兴趣的可以了解一下
    2022-05-05
  • 关于C++静态成员函数访问非静态成员变量的问题

    关于C++静态成员函数访问非静态成员变量的问题

    静态成员函数不能访问非静态成员,这是因为静态函数属于类而不是属于整个对象,静态函数中的 member可能都没有分配内存。静态成员函数没有隐含的this自变量。所以,它就无法访问自己类的非静态成员
    2013-10-10
  • VC实现给窗体的一个按钮添加事件的方法

    VC实现给窗体的一个按钮添加事件的方法

    这篇文章主要介绍了VC实现给窗体的一个按钮添加事件的方法,通过三个简单步骤实现窗体按钮添加事件,需要的朋友可以参考下
    2015-05-05
  • C语言带头双向循环链表的示例代码

    C语言带头双向循环链表的示例代码

    这篇文章主要介绍了如何利用C语言实现带头双向循环链表,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-11-11
  • C语言实现简单图书管理系统

    C语言实现简单图书管理系统

    这篇文章主要为大家详细介绍了C语言实现图书管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01

最新评论