使用QOpenGLWidget(调用GPU)渲染QImage加载的图片

举报
DS小龙哥 发表于 2022/02/28 09:31:42 2022/02/28
【摘要】 OpenGL是一个用来加速渲染显示2D、3D 矢量图形的编程接口。这个接口底层依赖于硬件GPU,底层硬件接口的驱动都是由GPU厂家提供。 openGl也支持跨平台,windows、Linux、MAC

一、OpenGL介绍

OpenGL是一个用来加速渲染显示2D、3D 矢量图形的编程接口。这个接口底层依赖于硬件GPU,底层硬件接口的驱动都是由GPU厂家提供。

openGl也支持跨平台,windows、Linux、MAC 平台都可以使用。

QT封装有QOpenGLWidget可以更加方便的调用GPU 来渲染图片。

下面例子代码就介绍QOpenGLWidget类的使用说明,并编写一个例子代码,调用GPU加速渲染一张QImage加载的图片。

二、QOpenGLWidget类介绍

image.png

下面是来至官方帮助的文档的介绍,我这里做了简单的调整,并翻译了一下:

image.png

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()是必需的。这是为了确保上下文之间的资源共享保持功能,因为所有内部上下文都是使用正确的版本和概要文件创建的。

绘画技巧

image.png

如上所述,子类QOpenGLWidget以以下方式呈现纯3D内容:

  1. 重新实现initializeGL()和resizeGL()函数,以设置OpenGL状态并提供透视转换。

  2. 重新实现paintGL()以绘制3D场景,仅调用OpenGL函数。

  3. 还可以使用QPaint在QOpenGLWidget子类上绘制2D图形:
    在paintGL()中,不要发出OpenGL命令,而是构造一个QPainter对象以在小部件上使用。

  4. 使用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重写的类)
image.png

image.png

#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"));
}

image.png

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。