使用QOpenGLWidget(调用GPU)渲染QImage加载的图片
一、OpenGL介绍
OpenGL是一个用来加速渲染显示2D、3D 矢量图形的编程接口。这个接口底层依赖于硬件GPU,底层硬件接口的驱动都是由GPU厂家提供。
openGl也支持跨平台,windows、Linux、MAC 平台都可以使用。
QT封装有QOpenGLWidget可以更加方便的调用GPU 来渲染图片。
下面例子代码就介绍QOpenGLWidget类的使用说明,并编写一个例子代码,调用GPU加速渲染一张QImage加载的图片。
二、QOpenGLWidget类介绍
下面是来至官方帮助的文档的介绍,我这里做了简单的调整,并翻译了一下:
QOpenGLWidget类是一个用于呈现OpenGL图形的小部件。
QOpenGLWidget提供了显示集成到Qt应用程序中的OpenGL图形的功能。它的使用非常简单:使你的类继承自它,并像任何其他QWidget一样使用子类,但你可以选择使用QPaint和标准OpenGL渲染命令。
QOpenGLWidget提供了三个方便的虚拟函数,你可以在子类中重新实现这些函数以执行典型的OpenGL任务:
paintGL()-渲染OpenGL场景。每当小部件需要更新时调用。
resizeGL()-设置OpenGL视口、投影等。每当小部件调整大小时(以及首次显示时,因为所有新创建的小部件都会自动获得调整大小事件),都会调用该小部件。
initializeGL()-设置OpenGL资源和状态。在第一次调用resizeGL()或paintGL()之前调用一次。
如果需要从paintGL()以外的位置触发重新绘制(典型示例是使用计时器设置场景动画),则应调用小部件的update()函数来安排更新。
调用paintGL()、resizeGL()或initializeGL()时,小部件的OpenGL呈现上下文将变为当前。如果需要从其他位置(例如,在小部件的构造函数或自己的绘制函数中)调用标准OpenGL API函数,则必须首先调用makeCurrent()。
所有渲染都发生在OpenGL帧缓冲区对象中。makeCurrent()确保它在上下文中绑定。在paintGL()中的渲染代码中创建和绑定其他帧缓冲区对象时,请记住这一点。永远不要重新绑定ID为0的帧缓冲区。相反,调用defaultFramebufferObject()获取应该绑定的ID。
QOpenGLWidget允许在平台支持时使用不同的OpenGL版本和配置文件。只需通过setFormat()设置请求的格式。但是请记住,在同一窗口中有多个QOpenGLWidget实例需要它们都使用相同的格式,或者至少使用不会使上下文不可共享的格式。要解决此问题,请首选使用QSurfaceFormat::setDefaultFormat()而不是setFormat()。
注意:在某些平台(例如macOS)上,当请求OpenGL核心概要文件上下文时,在构造QApplication实例之前调用QSurfaceFormat::setDefaultFormat()是必需的。这是为了确保上下文之间的资源共享保持功能,因为所有内部上下文都是使用正确的版本和概要文件创建的。
绘画技巧
如上所述,子类QOpenGLWidget以以下方式呈现纯3D内容:
重新实现initializeGL()和resizeGL()函数,以设置OpenGL状态并提供透视转换。
重新实现paintGL()以绘制3D场景,仅调用OpenGL函数。
还可以使用QPaint在QOpenGLWidget子类上绘制2D图形:
在paintGL()中,不要发出OpenGL命令,而是构造一个QPainter对象以在小部件上使用。使用QPaint的成员函数绘制基本体。仍然可以发出直接的OpenGL命令。但是,你必须确保调用画家的BeginativePainting()和endNativePainting()来包含这些内容。
当仅使用QPaint执行绘制时,也可以像对普通小部件一样执行绘制:通过重新实现paintEvent()。
重新实现paintEvent()函数。构造一个针对小部件的QPaint对象。将小部件传递给构造函数或QPaint::begin()函数。
使用QPaint的成员函数绘制基本体。绘制完成后,QPaint实例将被销毁。或者,显式调用QPaint::end()。
三、例子代码
3.1 头文件: 重载QOpenGLWidget
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QObject>
#include <QOpenGLWidget>
#include <GL/gl.h>
#include <GL/glu.h>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QGLWidget>
#include <QImage>
class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit MyGLWidget(QWidget *parent = 0);
signals:
public slots:
void initializeGL() Q_DECL_OVERRIDE;
void resizeGL(int w, int h) Q_DECL_OVERRIDE;
void paintGL() Q_DECL_OVERRIDE;
void setImage(const QImage &image);
void initTextures();
void initShaders();
private:
QVector<QVector3D> vertices;
QVector<QVector2D> texCoords;
QOpenGLShaderProgram program;
QOpenGLTexture *texture;
QMatrix4x4 projection;
};
#endif // MYGLWIDGET_H
3.2 源文件:QOpenGLWidget
#include "myglwidget.h"
#include <QDebug>
#include <QGraphicsOpacityEffect>
MyGLWidget::MyGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
}
void MyGLWidget::initTextures()
{
// 加载 Avengers.jpg 图片
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
//重复使用纹理坐标
//纹理坐标(1.1, 1.2)与(0.1, 0.2)相同
texture->setWrapMode(QOpenGLTexture::Repeat);
//设置纹理大小
texture->setSize(this->width(), this->height());
//分配储存空间
texture->allocateStorage();
}
void MyGLWidget::initShaders()
{
//纹理坐标
texCoords.append(QVector2D(0, 1)); //左上
texCoords.append(QVector2D(1, 1)); //右上
texCoords.append(QVector2D(0, 0)); //左下
texCoords.append(QVector2D(1, 0)); //右下
//顶点坐标
vertices.append(QVector3D(-1, -1, 1));//左下
vertices.append(QVector3D(1, -1, 1)); //右下
vertices.append(QVector3D(-1, 1, 1)); //左上
vertices.append(QVector3D(1, 1, 1)); //右上
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
const char *vsrc =
"attribute vec4 vertex;\n"
"attribute vec2 texCoord;\n"
"varying vec2 texc;\n"
"void main(void)\n"
"{\n"
" gl_Position = vertex;\n"
" texc = texCoord;\n"
"}\n";
vshader->compileSourceCode(vsrc);//编译顶点着色器代码
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
const char *fsrc =
"uniform sampler2D texture;\n"
"varying vec2 texc;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = texture2D(texture,texc);\n"
"}\n";
fshader->compileSourceCode(fsrc); //编译纹理着色器代码
program.addShader(vshader);//添加顶点着色器
program.addShader(fshader);//添加纹理碎片着色器
program.bindAttributeLocation("vertex", 0);//绑定顶点属性位置
program.bindAttributeLocation("texCoord", 1);//绑定纹理属性位置
// 链接着色器管道
if (!program.link())
close();
// 绑定着色器管道
if (!program.bind())
close();
}
void MyGLWidget::initializeGL()
{
initializeOpenGLFunctions(); //初始化OPenGL功能函数
glClearColor(0, 0, 0, 0); //设置背景为黑色
glEnable(GL_TEXTURE_2D); //设置纹理2D功能可用
initTextures(); //初始化纹理设置
initShaders(); //初始化shaders
}
void MyGLWidget::resizeGL(int w, int h)
{
// 计算窗口横纵比
qreal aspect = qreal(w) / qreal(h ? h : 1);
// 设置近平面值 3.0, 远平面值 7.0, 视场45度
const qreal zNear = 3.0, zFar = 7.0, fov = 45.0;
// 重设投影
projection.setToIdentity();
// 设置透视投影
projection.perspective(fov, static_cast<float>(aspect), zNear, zFar);
}
void MyGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕缓存和深度缓冲
QMatrix4x4 matrix;
matrix.translate(0.0, 0.0, -5.0); //矩阵变换
program.enableAttributeArray(0);
program.enableAttributeArray(1);
program.setAttributeArray(0, vertices.constData());
program.setAttributeArray(1, texCoords.constData());
program.setUniformValue("texture", 0); //将当前上下文中位置的统一变量设置为value
texture->bind(); //绑定纹理
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);//绘制纹理
texture->release(); //释放绑定的纹理
texture->destroy(); //消耗底层的纹理对象
texture->create();
}
void MyGLWidget::setImage(const QImage &image)
{
texture->setData(image); //设置纹理图像
//设置纹理细节
texture->setLevelofDetailBias(-1);//值越小,图像越清晰
update();
}
3.3 主函数: 调用测试
UI界面拖一个Qwidget控件,提升为MyGLwidget类(继承QopenGLWidget重写的类)
#include "widget.h"
#include "ui_widget.h"
#include "myglwidget.h"
#include <QGraphicsOpacityEffect>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//this->setWindowFlags(Qt::FramelessWindowHint);
//this->setWindowOpacity(0.5);
connect(&timer, &QTimer::timeout, this, &Widget::slotUpdate);
timer.start(40);
}
Widget::~Widget()
{
delete ui;
}
void Widget::slotUpdate()
{
ui->widget->setImage(QImage(":/image/1161.24.png"));
}
- 点赞
- 收藏
- 关注作者
评论(0)