QtQuick openGL 初步认识

Qt5.15.2 QML 自定义openGL的QuickItem初步认识

A.渲染过程

官方说明渲染过程->
常用的少了一个sceneGraphInvalidated -> 用于清空
在这里插入图片描述

常用信号及回调函数

  1. QQuickWindow::beforeSynchronizing -> sync:在同步场景图状态之前进行 OpenGL 资源的初始化或更新。
  2. QQuickWindow::sceneGraphInvalidated -> cleanup:在场景图无效时清理 OpenGL 资源。
  3. QQuickWindow::beforeRendering -> render:在开始渲染之前执行自定义的 OpenGL 绘制。
  4. QQuickWindow::frameSwapped -> onFrameSwapped:在交换帧缓冲区后准备下一帧的内容。
  5. QQuickWindow::openglContextCreated -> onOpenGLContextCreated:在 OpenGL 上下文创建后进行相关的初始化工作。

回调方式必须是直连 Qt::DirectConnection,因为openGL 本身就是状态机,所以必须直连处理。

信号及其作用和示例

1. QQuickWindow::beforeSynchronizing -> 回调函数 sync

作用:在同步场景图状态之前发出,用于进行OpenGL资源的初始化或更新。

示例

void Squircle::handleWindowChanged(QQuickWindow *win)
{
    if (win) {
        connect(win, &QQuickWindow::beforeSynchronizing, this, &Squircle::sync, Qt::DirectConnection);
        connect(win, &QQuickWindow::sceneGraphInvalidated, this, &Squircle::cleanup, Qt::DirectConnection);
        connect(win, &QQuickWindow::beforeRendering, this, &Squircle::render, Qt::DirectConnection);
        connect(win, &QQuickWindow::frameSwapped, this, &Squircle::onFrameSwapped, Qt::DirectConnection);
        connect(win, &QQuickWindow::openglContextCreated, this, &Squircle::onOpenGLContextCreated, Qt::DirectConnection);

        win->setColor(Qt::black);
    }
}

void Squircle::sync() {
    if (!m_initialized) {
        initializeOpenGLFunctions();
        // 初始化 OpenGL 资源,如 VBO、VAO、着色器等
        m_initialized = true;
    }
    // 更新需要同步的状态
}
2. QQuickWindow::sceneGraphInvalidated -> 回调函数 cleanup

作用:在场景图无效时发出,用于清理 OpenGL 资源。

示例

void Squircle::cleanup() {
    // 清理 OpenGL 资源,如 VBO、VAO、着色器等
    if (m_vbo) {
        delete m_vbo;
        m_vbo = nullptr;
    }
    if (m_vao) {
        delete m_vao;
        m_vao = nullptr;
    }
    if (m_shaderProgram) {
        delete m_shaderProgram;
        m_shaderProgram = nullptr;
    }
}
3. QQuickWindow::beforeRendering -> 回调函数 render

作用:在开始渲染之前发出,用于执行自定义的 OpenGL 绘制。

示例

void Squircle::render() {
    if (!m_initialized) return;

    // 自定义 OpenGL 绘制
    glBindVertexArray(m_vao);
    glUseProgram(m_shaderProgram->programId());
    
    // 设置着色器的统一变量
    m_shaderProgram->setUniformValue("u_color", QColor(Qt::red));

    // 绘制操作
    glDrawArrays(GL_TRIANGLES, 0, 3);
}
4. QQuickWindow::frameSwapped -> 回调函数 onFrameSwapped

作用:在交换帧缓冲区后发出,用于准备下一帧的内容。

示例

void Squircle::onFrameSwapped() {
    // 帧交换完成后的操作,通常用于准备下一帧的内容
    // 例如,更新动画状态,处理用户输入等
    qDebug() << "Frame swapped!";
}
5. QQuickWindow::openglContextCreated -> 回调函数 onOpenGLContextCreated

作用:在 OpenGL 上下文创建后发出,用于进行 OpenGL 相关的初始化工作。

示例

void Squircle::onOpenGLContextCreated(QOpenGLContext *context) {
    // OpenGL 上下文创建后的初始化操作
    initializeOpenGLResources(context);
    qDebug() << "OpenGL context created!";
}

void Squircle::initializeOpenGLResources(QOpenGLContext *context) {
    context->makeCurrent(window());
    initializeOpenGLFunctions();
    // 初始化 OpenGL 资源
    context->doneCurrent();
}

B.不同流程的渲染控制方式 QtQuick

0. QQuickPaintedItem

QQuickPaintedItem 是一种快速实现 OpenGL 渲染的方式。通过继承 QQuickPaintedItem 并设置渲染方式为 OpenGL,可以快速创建一个 OpenGL 控件。

使用方法:

  • 继承 QQuickPaintedItem
  • 重写 paint() 方法,在其中进行 OpenGL 渲染。

优点:

  • 快捷方便:最简单的 OpenGL 控件实现方式。
  • 快速集成:适合快速开发和测试。

缺点:

  • 性能较低:适合简单的渲染,不适用于复杂或高性能要求的场景。
1. QQuickFramebufferObject (FBO)

QQuickFramebufferObject 提供了一种在 QML 中嵌入自定义 OpenGL 渲染的简便方式。通过继承 QQuickFramebufferObject 并重载 Renderer 类的 render() 方法,可以实现自定义的 OpenGL 渲染。

控制过程:

  • 继承 QQuickFramebufferObject 并重载 createRenderer() 方法,返回一个自定义的 Renderer 实例。
  • Renderer 类中实现 render() 方法,进行 OpenGL 渲染。

优点:

  • 易于使用:封装了 OpenGL 的复杂细节,提供简洁的接口。
  • 直接集成:可以直接在 QML 中使用,适合快速开发。
  • 自动资源管理:自动管理 OpenGL 资源的创建和销毁。
  • 跨平台一致性:在不同平台上行为一致。

缺点:

  • 灵活性较低:相比直接使用 OpenGL,灵活性稍低。
  • 性能开销:封装带来一些性能开销,不适用于极高性能要求的场景。
2. QSGNode

通过继承 QQuickItem 并重载 updatePaintNode() 方法,可以直接操作 Qt Quick 的场景图。通过自定义 QSGNode 子类,可以实现更复杂的渲染逻辑。

控制过程:

  • 继承 QQuickItem 并重载 updatePaintNode(QSGNode *, UpdatePaintNodeData *) 方法。
  • 自定义 QSGNode 子类,实现节点的渲染逻辑。

优点:

  • 高性能:利用 Qt Quick 的场景图优化,适合高性能渲染。
  • 细粒度控制:对渲染过程有更细粒度的控制,适合复杂渲染效果。
  • 增量更新:可以单独更新某些节点,节省资源。
  • 与 QML 无缝集成:适合在 QML 环境中使用,并能很好地结合其他 QML 元素。

缺点:

  • 复杂性较高:需要深入了解 Qt Quick 场景图的工作机制。
  • 手动资源管理:需要手动管理 OpenGL 资源,容易出错。
3. QOpenGLFunctions

直接使用 QOpenGLFunctions 提供了对 OpenGL 的完全控制。通过继承 QOpenGLFunctions 并在渲染过程中调用 OpenGL 函数,可以实现任何自定义的 OpenGL 渲染逻辑。

控制过程:

  • 继承 QOpenGLFunctions
  • 在渲染过程中调用 OpenGL 函数,实现自定义渲染逻辑。

优点:

  • 最大灵活性:完全控制渲染过程,适合复杂和定制化的渲染效果。
  • 高性能潜力:优化得当,能够实现最高性能。
  • 无缝集成:可以与现有的 OpenGL 代码库和工具无缝集成。

缺点:

  • 复杂性最高:需要手动管理所有 OpenGL 资源,学习曲线陡峭。
  • 易出错:直接操作 OpenGL,容易发生资源泄漏和其他错误。
  • 集成复杂:与 Qt Quick 的集成需要更多工作以确保一致性和性能。

代码示例

0. QQuickPaintedItem
#include <QQuickPaintedItem>
#include <QPainter>

class MyQuickPaintedItem : public QQuickPaintedItem
{
    Q_OBJECT
public:
    MyQuickPaintedItem(QQuickItem *parent = nullptr) : QQuickPaintedItem(parent) {}

    void paint(QPainter *painter) override
    {
        // 在这里进行OpenGL渲染
    }
};
1. QQuickFramebufferObject【推荐】
#include <QQuickFramebufferObject>

class MyFramebufferObjectRenderer : public QQuickFramebufferObject::Renderer
{
public:
    void render() override
    {
        // 在这里进行OpenGL渲染
    }
};

class MyFramebufferObject : public QQuickFramebufferObject
{
    Q_OBJECT
public:
    QQuickFramebufferObject::Renderer *createRenderer() const override
    {
        return new MyFramebufferObjectRenderer();
    }
};
2. QSGNode【推荐】
#include <QQuickItem>
#include <QSGNode>

class MyQuickItem : public QQuickItem
{
    Q_OBJECT
public:
    MyQuickItem(QQuickItem *parent = nullptr) : QQuickItem(parent) {}

    QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override
    {
        // 在这里进行场景图节点的更新和渲染
        return oldNode;
    }
};
3. QOpenGLFunctions
#include <QOpenGLFunctions>
#include <QQuickItem>

class MyOpenGLFunctions : public QQuickItem, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    MyOpenGLFunctions(QQuickItem *parent = nullptr) : QQuickItem(parent)
    {
        initializeOpenGLFunctions();
    }

    void paint()
    {
        // 在这里进行OpenGL渲染
    }
};
4. QOpenGLWindow

QOpenGLWindow 是一种用于 OpenGL 渲染的窗口,它继承自 QWindow 并提供了 OpenGL 渲染的接口。

使用方法:

  • 继承 QOpenGLWindow
  • 重写 initializeGL()resizeGL()paintGL() 方法。
5. QOpenGLWidget

QOpenGLWidget 是 Qt 中用于集成 OpenGL 渲染的一个常用控件,它提供了一个方便的接口来进行 OpenGL 渲染。

使用方法:

  • 继承 QOpenGLWidget
  • 重写 initializeGL()resizeGL()paintGL() 方法。

PS:
单纯使用QQuickItem+QOpenGLFunctions,QQuickWindow::beforeRendering绑定回调的方式
即,使用opengl原生的方式和回调的方式,有一个问题是只能在qml的最上面或者最下面。不推荐使用!!!

3、图形渲染管线

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/8bfe493890134e1583e45e0811374507.png

  • 顶点数组对象:Vertex Array Object,VAO
  • 顶点缓冲对象:Vertex Buffer Object,VBO
  • 元素缓冲对象:Element Buffer Object,EBO 或 索引缓冲对象 Index Buffer Object,IBO

VBO 在GPU上创建的内存大小及地址

1、顶点着色器
2、片段着色器
3、创建着色器程序
4、链接
5、启用
6、属性设置

4、投射矩阵

4.1 正交投影 - 2D

4.2 透视投影 - 3D

Model View Projection - MVP矩阵

Model

View

Projection

5、摄像头

6、绘制基本方法

7、绘制圆

在片段着色器绘制圆,效率更高

uniform highp vec2 u_resolution;
varying lowp vec4 color;

void main() {
    // 将像素坐标转换为标准化坐标 (0,1)
    vec2 st = gl_FragCoord.xy / u_resolution;
    // 初始化背景颜色为透明
    vec4 backgroundColor = vec4(0.0, 0.0, 0.0, 0.0);
    // 计算当前像素与圆心的距离
    float distanceFromCenter = distance(st, vec2(0.5, 0.5));
    // 使用 smoothstep 函数来实现平滑的边缘
    float circle = smoothstep(0.3, 0.305, distanceFromCenter);
    // 计算圆内的颜色,红色
    vec3 circleColor = vec3(1.0, 1.0, 0.0);
    // 混合圆的颜色和背景颜色
    vec3 color = mix(circleColor, backgroundColor.rgb, circle);
    // 设置片段颜色,调整透明度
    gl_FragColor = vec4(color, 1.0 - circle);
}

8、example

  • 绘制三角形
  • 绘制图片
  • 绘制矩形、三角形、圆点、图片
  • 鼠标点击创建一个圆点
  • 鼠标点击绘制多边形
  • 鼠标控制旋转立方体
  • 加载3D物体
  • 光照 - 100个立方体,可鼠标点击转换视角
  • 光照 - 土星环
  • 上帝视角 - 小镇风光

摄像头
gluLookAt,gluPerspective和glOrtho

gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear,GLdouble zFar);
观察的视景体在世界坐标系中的具体大小,一般而言,其中的参数aspect应该与窗口的宽高比大小相同。
fovy,视野角度,用过照相机的话就很好理解了,数值越小,相当于将镜头拉得越近,数值越大,镜头越广,镜头里的东西就越小。
aspect,这个好理解,就是实际窗口的纵横比,即x/y。
zNear,这个呢,表示你近处,的裁面。
zFar表示远处的裁面。
glLoadIdentity();
这个函数类似于一个复位操作:
1.X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。
2.OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。
3.中心左面的坐标值是负值,右面是正值。
移向屏幕顶端是正值,移向屏幕底端是负值。
移入屏幕深处是负值,移出屏幕则是正值。

glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
这个函数设置清除屏幕时所用的颜色,色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
glTranslatef(x, y, z)沿着 X, Y 和 Z 轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。注意在glTranslatef(x, y, z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。

glVertex *
glVertex3f 就是确定顶点的函数,三个参数是点 的空间坐标。

void perspective(float verticalAngle, float aspectRatio, float nearPlane, float farPlane);

在这里插入图片描述
在这里插入图片描述

投影变换和视景体
http://t.csdnimg.cn/Js7Hf

Qt6 有更好的QRHI ,但是目前不成熟,可以切换渲染平台,包括opengl,dirext,vulkan等。
Qt5 的openGL ES用在ios,有版权风险。用在机器人好像也有风险,dll链接也是。

  • 绘制圆点的两种方法

https://stackoverflow.com/questions/4197062/using-the-following-function-that-draws-a-filled-circle-in-opengl-how-do-i-make

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值