本页内容

场景图 - QML 下的 Vulkan

演示如何在Qt Quick 场景中直接使用Vulkan进行渲染。

在 Qt Quick 场景中使用 Vulkan 渲染的带文字叠加的动画“椭圆”

注意:编译 此示例需要 SDK。有关需要安装的内容,请参阅“Vulkan 集成”。

概述

本示例利用QQuickWindow::beforeRendering()和QQuickWindow::beforeRenderPassRecording()信号,在Qt Quick 场景下绘制自定义的Vulkan内容。同时使用QML在顶层渲染一个文本标签。

该示例在多数方面与“OpenGL Under QML”、“Direct3D 11 Under QML”和“Metal Under QML”示例等效,它们都渲染相同的自定义内容,只是通过不同的原生 API 实现。

本文档将重点介绍 QML 的具体用法,而不会深入探讨自定义 Vulkan 渲染的细节。

从 QML 影响 Vulkan 渲染

该示例展示了如何使用暴露给 QML 的值来控制 Vulkan 渲染。

为了将阈值t 暴露给QML,在VulkanSquircle 的定义中,我们像这样使用Q_OBJECT 、Q_PROPERTY 和QML_ELEMENT 宏:

class VulkanSquircle : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
    QML_ELEMENT

接着,我们声明 public 和 private 项:

public:
    VulkanSquircle();

    qreal t() const { return m_t; }
    void setT(qreal t);

signals:
    void tChanged();

public slots:
    void sync();
    void cleanup();

private slots:
    void handleWindowChanged(QQuickWindow *win);

private:
    void releaseResources() override;

    qreal m_t = 0;
    SquircleRenderer *m_renderer = nullptr;

接着在main.qml 中,我们使用NumberAnimation 对阈值进行动画处理。

    VulkanSquircle {
        SequentialAnimation on t {
            NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
            NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
            loops: Animation.Infinite

该t 变量最终会被SPIR-V着色器程序调用,用于绘制椭圆形。

使用信号渲染自定义 Vulkan 内容

这里使用的是 `QQuickWindow::beforeRendering()` 和 `QQuickWindow::beforeRenderPassRecording()` 信号。

QQuickWindow::beforeRendering() 信号在每帧开始时触发,即在场景图开始渲染之前。这意味着作为对此信号响应而发出的任何 Vulkan 绘制调用,都将堆叠在Qt Quick 项之下。之所以有这两个信号,是因为自定义 Vulkan 命令会被记录到与场景图所用相同的命令缓冲区中。

仅靠 beforeRendering() 函数无法实现这一目的,因为它是在帧开始时触发的,而此时尚未通过vkCmdBeginRenderPass 记录renderpass 实例的开始。

解决方案:通过连接到 beforeRenderPassRecording(),应用程序自身的命令和场景图的辅助结构最终将按正确顺序排列。

信号的连接通过 `sync() ` 函数实现:

void VulkanSquircle::sync()
{
    if (!m_renderer) {
        m_renderer = new SquircleRenderer;
        // Initializing resources is done before starting to record the
        // renderpass, regardless of wanting an underlay or overlay.
        connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection);
        // Here we want an underlay and therefore connect to
        // beforeRenderPassRecording. Changing to afterRenderPassRecording
        // would render the squircle on top (overlay).
        connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection);
    }
    m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
    m_renderer->setT(m_t);

另一种在Qt Quick 场景上渲染Vulkan内容的方法是连接到QQuickWindow::afterRendering()和QQuickWindow::afterRenderPassRecording()信号。

示例项目 @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.