从零开始:在Qt中使用OpenGL绘制指南
从零开始:在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)的实现存在差异。例如:
| 平台 | 函数加载方式 |
|---|---|
| Windows | wglGetProcAddress |
| Linux | glXGetProcAddress |
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. 加载 glClearglClear = (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_OBJECTpublic: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绘制指南 本文只介绍基本的 QOpenGLWidget 和 QOpenGLFunctions 的使用,想要学习 OpenGL 的朋友,建议访问经典 OpenGL 学习网站:LearnOpenGL CN 本篇文章,我们将以绘制一个经典的三角形为例&…...
激光加工中平面倾斜度的矫正
在激光加工中,加工平面的倾斜度矫正至关重要,直接影响加工精度和材料处理效果。以下是系统的矫正方法和步骤: 5. 验证与迭代 二次测量:加工后重新检测平面度,确认残余误差。 反馈优化:根据误差分布修正补偿…...
Android学习总结之应用启动流程(从点击图标到界面显示)
一、用户交互触发:Launcher 到 AMS 的跨进程通信 1. Launcher 处理点击事件(应用层) 当用户点击手机桌面上的应用图标时,Launcher(桌面应用)首先捕获点击事件。每个图标对应一个启动 Intent(通…...
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结构(十五)系统调用与函数地址动态寻找
双机调试 当需要分析一个程序时,这个程序一定是可以调试的,操作系统也不例外。在调试过程中下断点是很重要的 当我们对一个应用程序下断点时,应用程序是挂起的。但当我们对操作系统的内核程序下断点时,被挂起的不是内核程序而是…...
webrtc 本地运行的详细操作步骤 1
前言 选修课的一个课程设计,需要我们本地运行这个开源项目,给我的压力非常大,因为确实不是很熟练这种操作。但是还是得做。谨以此文,纪念这个过程。 之前自己在 github 上面看到有代码仓库,但是比较复杂,在…...
kali——httrack
目录 前言 使用教程 前言 HTTrack 是一款运行于 Kali Linux 系统中的开源网站镜像工具,它能将网站的页面、图片、链接等资源完整地下载到本地,构建出一个和原网站结构相似的离线副本。 使用教程 apt install httrack //安装httrack工具 httrac…...
力扣经典算法篇-6-轮转数组
题干: 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步…...
【计算机网络】Linux配置SNAT/DNAT策略
什么是NAT? NAT 全称是 Network Address Translation(网络地址转换),是一个用来在多个设备共享一个公网 IP上网的技术。 NAT 的核心作用:将一个网络中的私有 IP 地址,转换为公网 IP 地址,从而…...
火山引擎coze用户市场
火山引擎 **Coze**(扣子)的用户市场主要集中在 **需要快速构建和部署智能对话应用的企业及开发者群体**,覆盖多个行业与场景。以下是具体分析: --- ### **一、核心用户群体** 1. **企业用户** - **互联网/科技公司**&#…...
qt designer 软件主题程序设计
对于使用Qt Designer设计的界面,主题切换的实现需要结合Qt的信号槽机制、样式表动态加载以及资源管理。以下是针对Qt Designer UI的详细解决方案: 一、UI文件与主题系统的整合架构 二、核心实现步骤 1. 动态样式表加载系统 // ThemeManager.h class …...
2025/4/2 心得
第一题 题目描述 给定1001个范围在[1,1000]的数字,保证只有1个数字重复出现2次,其余数字只出现1次。试用O(n)时间复杂度来求出出现2次的这个数字。 不允许用数组 输入格式 第一行:一个整数1001; 第二行:1001个用…...
AI安全:构建负责任且可靠的系统
AI已成为日常生活中无处不在的助力,随着AI系统能力和普及性的扩展,安全因素变得愈发重要。从基础模型构建者到采用AI解决方案的企业,整个AI生命周期中的所有相关方都必须共同承担责任。 为什么AI安全至关重要? 对于企业而言&…...
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 中构建实时通知
示例代码:https://download.csdn.net/download/hefeng_aspnet/90448094 介绍 构建实时应用程序已成为现代 Web 开发中必不可少的部分,尤其是对于通知、聊天系统和实时更新等功能。SignalR 是 ASP.NET 的一个强大库,可实现服务器端代码和客户…...
Kotlin 集合函数:map 和 first 的使用场景
Kotlin 提供了丰富的集合操作函数,使开发者可以更加简洁、高效地处理数据。其中,map 和 first 是两个常用的函数,分别用于转换集合和获取集合中的第一个元素。 1. map 的使用场景 场景 1:对象列表转换 在开发中,我们…...
Spring Cloud 框架为什么能处理高并发
Spring Cloud框架能够有效处理高并发场景,核心在于其微服务架构设计及多组件的协同作用,具体机制如下: 一、分布式架构设计支撑高扩展性 服务拆分与集群部署 Spring Cloud通过微服务拆分将单体系统解耦为独立子服务,每个服务可独…...
【Python爬虫高级技巧】BeautifulSoup高级教程:数据抓取、性能调优、反爬策略,全方位提升爬虫技能!
大家好,我是唐叔!上期我们聊了 BeautifulSoup的基础用法 ,今天带来进阶篇。我将分享爬虫老司机总结的BeautifulSoup高阶技巧,以及那些官方文档里不会告诉你的实战经验! 文章目录 一、BeautifulSoup性能优化技巧1. 解析…...
复古未来主义屏幕辉光像素化显示器反乌托邦效果PS(PSD)设计模板样机 Analog Retro-Futuristic Monitor Effect
这款模拟复古未来主义显示器效果直接取材于 90 年代赛博朋克电影中的黑客巢穴,将粗糙的屏幕辉光和像素化的魅力强势回归。它精准地模仿了老式阴极射线管显示器,能将任何图像变成故障频出的监控画面或高风险的指挥中心用户界面。和……在一起 2 个完全可编…...
Spring Boot + MySQL + MyBatis(注解和XML配置两种方式)集成Redis的完整启用及配置详解,包含代码示例、注释说明和表格总结
以下是 Spring Boot MySQL MyBatis(注解和XML配置两种方式)集成Redis的完整启用及配置详解,包含代码示例、注释说明和表格总结: 1. 添加依赖 在pom.xml中添加Spring Boot对MySQL、MyBatis和Redis的支持依赖: <d…...
Webpack vs Vite:现代前端构建工具的巅峰对决与选型指南
构建工具的进化革命当雪碧瓶上的水珠折射出前端工程的变迁史,Webpack与Vite的决战已然成为现代前端开发的分水岭。这场始于打包理念的革命,正在重塑整个前端生态的底层逻辑。本文将从原理架构、性能表现、开发体验三个维度,结合真实项目数据对…...
2023-2024总结记录
概括经历 这一年算是一个人生节点,2023年花了一整年的时间在准备考研,基本上等于一个人奋战,我不怎么去图书馆,只呆在无人的实验室,还好有对象陪我,不然可能要抑郁了。作息上还是很随意,什么时…...
技术驱动革新,强力巨彩LED软模组助力创意显示
随着LED显示技术的不断突破,LED软模组因其独特的柔性特质和个性化显示效果,正逐渐成为各类应用场景的新宠。强力巨彩软模组R3.0H系列具备独特的可塑造型能力与技术创新,为商业展示、数字艺术、建筑装饰等领域开辟全新视觉表达空间。 LED…...
Spring 核心技术解析【纯干货版】- XVIII:Spring 网络模块 Spring-WebSocket 模块精讲
在现代 Web 开发中,实时通信已成为提升用户体验的关键技术之一。传统的 HTTP 轮询方式存在较高的延迟和带宽开销,而 WebSocket 作为一种全双工通信协议,能够在客户端和服务器之间建立持久连接,实现高效的双向数据传输。 Spring 框…...
Spark,HDFS概述
HDFS组成构架: 注: NameNode(nn):就是 Master,它是一个主管、管理者。 (1) 管理 HDFS 的名称空间; (2) 配置副本策略。记录某些文件应该保持几个副本; (3) 管理数据块(…...
【数据结构】图论进阶:生成树、生成森林与权值网络的终极解析
图的基本概念 导读一、图中的树与森林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)的链表初步
链表是构建其他复杂数据结构的基础,如栈、队列、图和哈希表等。通过对链表进行适当的扩展和修改,可以实现这些数据结构的功能。想学算法,数据结构,不会链表是万万不行的。这篇笔记是一名小白在学习时整理的。 C语言 链表部分 …...
深入浅出 TypeScript 泛型:类型安全的艺术与实践
文章目录 一、泛型的核心概念1.1 类型参数:代码中的类型变量1.2 类型推断:让代码保持简洁 二、泛型的四大应用场景2.1 泛型函数:打造通用工具库2.2 泛型接口:定义灵活的数据结构2.3 泛型类:构建类型安全的容器2.4 泛型…...
【KWDB创作者计划】_KaiwuDB 2.1.0 单节点裸机部署
大家好,这里是 DBA学习之路,专注于提升数据库运维效率。 目录 前言KWDB 介绍安装准备环境信息配置要求操作系统软件依赖端口要求安装包下载 部署 KWDB简单实用连接数据库创建数据库创建用户创建时序表 前言 今天无意间在墨天轮看到一个征文活动 征文大赛…...
洛谷题单3-P5720 【深基4.例4】一尺之棰-python-流程图重构
题目描述 《庄子》中说到,“一尺之棰,日取其半,万世不竭”。第一天有一根长度为 a a a 的木棍,从第二天开始,每天都要将这根木棍锯掉一半(每次除 2 2 2,向下取整)。第几天的时候木…...
