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

news2025/4/8 22:31:14

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

本文只介绍基本的 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");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2329524.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

激光加工中平面倾斜度的矫正

在激光加工中&#xff0c;加工平面的倾斜度矫正至关重要&#xff0c;直接影响加工精度和材料处理效果。以下是系统的矫正方法和步骤&#xff1a; 5. 验证与迭代 二次测量&#xff1a;加工后重新检测平面度&#xff0c;确认残余误差。 反馈优化&#xff1a;根据误差分布修正补偿…

rdiff-backup备份

目录 1. 服务器备份知识点 1.1 备份策略 1.2 备份步骤和宝塔面板简介 1.3 CentOS7重要目录 2. 备份工具 2.1 tar -g 备份演示 2. rsync 备份演示 3. rdiff-backup 备份演示 4. 差异和优缺点 3. rdiff-backup安装和使用 3.1 备份命令rdiff-backup 3.2 恢复命令--…

PE结构(十五)系统调用与函数地址动态寻找

双机调试 当需要分析一个程序时&#xff0c;这个程序一定是可以调试的&#xff0c;操作系统也不例外。在调试过程中下断点是很重要的 当我们对一个应用程序下断点时&#xff0c;应用程序是挂起的。但当我们对操作系统的内核程序下断点时&#xff0c;被挂起的不是内核程序而是…

webrtc 本地运行的详细操作步骤 1

前言 选修课的一个课程设计&#xff0c;需要我们本地运行这个开源项目&#xff0c;给我的压力非常大&#xff0c;因为确实不是很熟练这种操作。但是还是得做。谨以此文&#xff0c;纪念这个过程。 之前自己在 github 上面看到有代码仓库&#xff0c;但是比较复杂&#xff0c;在…

kali——httrack

目录 前言 使用教程 前言 HTTrack 是一款运行于 Kali Linux 系统中的开源网站镜像工具&#xff0c;它能将网站的页面、图片、链接等资源完整地下载到本地&#xff0c;构建出一个和原网站结构相似的离线副本。 使用教程 apt install httrack //安装httrack工具 httrac…

【计算机网络】Linux配置SNAT/DNAT策略

什么是NAT&#xff1f; NAT 全称是 Network Address Translation&#xff08;网络地址转换&#xff09;&#xff0c;是一个用来在多个设备共享一个公网 IP上网的技术。 NAT 的核心作用&#xff1a;将一个网络中的私有 IP 地址&#xff0c;转换为公网 IP 地址&#xff0c;从而…

AI安全:构建负责任且可靠的系统

AI已成为日常生活中无处不在的助力&#xff0c;随着AI系统能力和普及性的扩展&#xff0c;安全因素变得愈发重要。从基础模型构建者到采用AI解决方案的企业&#xff0c;整个AI生命周期中的所有相关方都必须共同承担责任。 为什么AI安全至关重要&#xff1f; 对于企业而言&…

VUE+SPRINGBOOT+语音技术实现智能语音歌曲管理系统

语音控制歌曲的播放、暂停、增删改查 <template><div class"Music-container"><div style"margin: 10px 0"><!--检索部分--><el-input style"width: 200px;" placeholder"请输入歌曲名称"v-model"sen…

使用 SignalR 在 .NET Core 8 最小 API 中构建实时通知

示例代码&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90448094 介绍 构建实时应用程序已成为现代 Web 开发中必不可少的部分&#xff0c;尤其是对于通知、聊天系统和实时更新等功能。SignalR 是 ASP.NET 的一个强大库&#xff0c;可实现服务器端代码和客户…

复古未来主义屏幕辉光像素化显示器反乌托邦效果PS(PSD)设计模板样机 Analog Retro-Futuristic Monitor Effect

这款模拟复古未来主义显示器效果直接取材于 90 年代赛博朋克电影中的黑客巢穴&#xff0c;将粗糙的屏幕辉光和像素化的魅力强势回归。它精准地模仿了老式阴极射线管显示器&#xff0c;能将任何图像变成故障频出的监控画面或高风险的指挥中心用户界面。和……在一起 2 个完全可编…

技术驱动革新,强力巨彩LED软模组助力创意显示

随着LED显示技术的不断突破&#xff0c;LED软模组因其独特的柔性特质和个性化显示效果&#xff0c;正逐渐成为各类应用场景的新宠。强力巨彩软模组R3.0H系列具备独特的可塑造型能力与技术创新&#xff0c;为商业展示、数字艺术、建筑装饰等领域开辟全新视觉表达空间。    LED…

Spark,HDFS概述

HDFS组成构架&#xff1a; 注&#xff1a; NameNode&#xff08;nn&#xff09;&#xff1a;就是 Master&#xff0c;它是一个主管、管理者。 (1) 管理 HDFS 的名称空间&#xff1b; (2) 配置副本策略。记录某些文件应该保持几个副本&#xff1b; (3) 管理数据块&#xff08;…

【数据结构】图论进阶:生成树、生成森林与权值网络的终极解析

图的基本概念 导读一、图中的树与森林1.1 生成树与生成森林1.1.1 生成树1.1.2 生成森林1.1.3 生成树、生成森林与连通分量结点的关系边的关系 1.2 有向图中的树与森林1.2.1 有向树与有向森林1.2.2 生产有向树与生成有向森林1.2.3 有向树与生成有向树的区别1.2.4 有向森林与生成…

C和C++(list)的链表初步

链表是构建其他复杂数据结构的基础&#xff0c;如栈、队列、图和哈希表等。通过对链表进行适当的扩展和修改&#xff0c;可以实现这些数据结构的功能。想学算法&#xff0c;数据结构&#xff0c;不会链表是万万不行的。这篇笔记是一名小白在学习时整理的。 C语言 链表部分 …

【KWDB创作者计划】_KaiwuDB 2.1.0 单节点裸机部署

大家好&#xff0c;这里是 DBA学习之路&#xff0c;专注于提升数据库运维效率。 目录 前言KWDB 介绍安装准备环境信息配置要求操作系统软件依赖端口要求安装包下载 部署 KWDB简单实用连接数据库创建数据库创建用户创建时序表 前言 今天无意间在墨天轮看到一个征文活动 征文大赛…

前端快速入门学习3——CSS介绍与选择器

1.概述 CSS全名是cascading style sheets,中文名层叠样式表。 用于定义网页样式和布局的样式表语言。 通过 CSS&#xff0c;你可以指定页面中各个元素的颜色、字体、大小、间距、边框、背景等样式&#xff0c;从而实现更精确的页面设计。 HTML与CSS的关系&#xff1a;HTML相当…

Redash:一个开源的数据查询与可视化工具

Redash 是一款免费开源的数据可视化与协作工具&#xff0c;可以帮助用户快速连接数据源、编写查询、生成图表并构建交互式仪表盘。它简化了数据探索和共享的过程&#xff0c;尤其适合需要团队协作的数据分析场景。 数据源 Redash 支持各种 SQL、NoSQL、大数据和 API 数据源&am…

嵌入式Linux驱动—— 1 GPIO配置

目录 1.GPIO操作 1.1 IO命名 1.2 GPIO 时钟使能&#xff08;CCM&#xff09; 1.3 IO 复用&#xff08;IOMUXC&#xff09; 1.4 IO 配置 1.5 GPIO 配置 1.GPIO操作 GPIO操作主要是以下流程&#xff1a; 使能某组GPIO模块&#xff08;GPIO1、2、...&#xff09;&#…

[ICLR 2025]Biologically Plausible Brain Graph Transformer

论文网址&#xff1a;Biologically Plausible Brain Graph Transformer 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 …

SpringBoot+MyBatis Plus+PageHelper+vue+mysql 实现用户信息增删改查功能

静态资源展示 &#xff08;1&#xff09;静态资源下载 &#xff08;2&#xff09;下载后文件放到resources/static 目录下 (3) main函数启动项目访问对应文件&#xff0c;http://127.0.0.1:8080/user-list.html 数据库添加表和数据 SET FOREIGN_KEY_CHECKS0;-- --------…