从零开始:在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. 加载 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绘制指南的更多相关文章
- Ubuntu中在QT中配置OpenGL
之前搞实验室项目,博客有些天没有更新.现在学习需要,开始搞OpenGL+Ubuntu+QT. 搞了整整一天,由于是首次使用ubuntu,所以这ubuntu下配置qt和Opengl环境时走了很多的弯路, ...
- QT中文字的绘制
为什么要做这次文字的介绍,因为在一般的教材中,还真没有文字的描述: 1.绘制最简单的文字. 我们更改重绘函数如下: void Dialog::paintEvent(QPaintEvent *){QPa ...
- 46.Qt 使用OpenGL绘制立方体
main.cpp #include <QApplication> #include <iostream> #include "vowelcube.h" in ...
- Qt Examples - Boxes (在Qt场景视图中结合OpenGL渲染)
QT自带例程Boxes使用QT Graphics View框架实现了2D图形和3D图形的混合渲染,综合性比较强,整合知识较多,值得学习. 可以使用鼠标通过以下方式控制演示中的元素: 按住鼠标左键的同时 ...
- opengl es中不同的绘制方式
opengl es中不同的绘制方式 转载请保留出处:http://xiaxveliang.blog.163.com/blog/static/297080342013467344263/ 1. GL_P ...
- Qt 框架的图形性能高(OpenGL上的系统效率高),网络性能低,开发效率高,Quick是可以走硬件加速——Qt中分为好几套图形系统,差不多代表了2D描画的发展史。最经典的软描画系统
-----图形性能部分-----Qt的widgets部分,运行时的图像渲染性能是一般的,因为大部分的界面内容都是Qt自绘,没有走硬件加速,也就是说很多图形内容都是CPU算出来的.但是widgets底层 ...
- Qt中OpenGL的初步使用
结果预览: 一.代码5个文件 //glwidget.h #ifndef GLWIDGET_H #define GLWIDGET_H #include <QGLWidget> class G ...
- 在Android中使用OpenGL ES进行开发第(三)节:绘制图形
一.前期基础知识储备笔者计划写三篇文章来详细分析OpenGL ES基础的同时也是入门关键的三个点: ①OpenGL ES是什么?与OpenGL的关系是什么?——概念部分 ②使用OpenGLES绘制2D ...
- qt中窗口绘制——图片的绘制
在qt 中,QPixmap 用于表示一张图片,支持png,jpg格式的加载. QPixmap pm("c:/test.png"); 或者 QPixmap pm; pm.load(& ...
- OpenGL编程指南(第七版)
OpenGL编程指南(第七版) 转自:http://blog.csdn.net/w540982016044/article/details/21287645 在接触OpenGL中,配置显得相当麻烦,特 ...
随机推荐
- 一级缓存和二级缓存--mybatis|hibernate
一级缓存和二级缓存的区别: 主要的不同是它们的作用范围不同. 一级缓存是session级别的. 也就是只有在同一个session里缓存才起作用,当这个session关闭后这个缓存就不存在了. 而二级缓 ...
- springboot之结合mybatis增删改查解析
1. 场景描述 本节结合springboot2.springmvc.mybatis.swagger2等,搭建一个完整的增删改查项目,希望通过这个基础项目,能帮忙朋友快速上手springboot2项目. ...
- Flume架构与源码分析-整体架构
最近在学习Flume源码,所以想写一份Flume源码学习的笔记供需要的朋友一起学习参考. 1.Flume介绍 Flume是cloudera公司开源的一款分布式.可靠地进行大量日志数据采集.聚合和并转移 ...
- ORACLE 中报ORA-30926 无法在源表中获得稳定的行的处理
在库存处理的业务中有这么一个场景,一张处方划价单进行库存扣减处理,如果此单据同一商品有两行以上,同时扣减同一行库存记录,使用MERGE INTO批量更新是就会报错:ORA-30926 无法在源表中获得 ...
- 创建react脚手架(项目)
现在安装 react 脚手架的有三种, react-boilerplate react-redux-starter-kit create-react-app(国内主要运用这一种) 1==>npm ...
- 面试官:说说你项目中JWT的执行流程?
JWT 在目前的项目开发中使用到的频率是非常高的,因此它也是面试常问的一类问题,所以今天我们就来看看"项目中 JWT 的执行流程?"这个问题. 1.什么是 JWT? JWT(JSO ...
- C语言中的*和&符号
之前对*和&符号一直理解的比较浅显.只知道: *p好像表示的是一个指针: &p表示的是一个地址. 然而这次当遇到了下面这个情况的时候: int a = 10; int *b = &am ...
- 【Blender】杂项笔记
[Blender]杂项笔记 空间坐标系 Blender 中的轴向: Y 轴向前(前视图看向的方向就是前方,其默认向 Y 轴看) Z 轴向上 保持轴向导出到 Unity 时(包括直接保存.导出 FBX ...
- mybatis - [09] 动态SQL
题记部分 一.if & test 如果id,name,age不为空,则按照指定的值进行查询.如果这三者都是空(null和空字符串),则该sql执行结果为全表查询的结果集. <select ...
- mybatis - [08] mybatis-config.xml 详解
mybatis-config.xml中的标签需要按照一定顺序配置,否则会有以下提示. configuration(配置) properties(属性) settings(设置) typeAliases ...