シーングラフ - カスタムQSGRenderNode

Qt Quick シーングラフでカスタムレンダリングを実装するためにQSGRenderNode を使用する方法を示します。

カスタムレンダーノードの例では、QSGRenderNode から派生したシーングラフノードによってバックされ、独自のQRhi ベースのレンダリングを提供するQQuickItem サブクラスの実装方法を示しています。

注意: この例では、Qt GUI モジュールの限定的な互換性保証の API に依存しながら、ポータブルでクロスプラットフォームな 3D レンダリングを実行する、高度で低レベルの機能を示しています。QRhi API を使用できるように、アプリケーションはQt::GuiPrivate にリンクし、<rhi/qrhi.h> をインクルードしています。

QSGRenderNode は、シーングラフ内の Render Hardware Interface(RHI)への直接アクセスを可能にします。この例では、 ベースのレンダーノードを作成し、カスタムアイテムで管理する方法を示します。レンダーノードはRHIパイプラインを作成し、頂点バッファとユニフォームバッファを更新し、RHIコマンドバッファにレンダリングします。QSGRenderNode

実際には、これはOpenGL、Metal、Vulkanなどのネイティブ3D APIに頼ることなく、シーングラフ独自のレンダリングとインラインでカスタムレンダリングを実行する、移植性の高いクロスプラットフォームのアプローチです。むしろ、アプリケーションはQtのグラフィックスとシェーダーの抽象化レイヤーを使用します。

QSGRenderNode は、 シーンにカスタム 2D/3D レンダリングを統合する 3 つの方法のうちの 1 つを実現するものです。他の2つのオプションは、 または シーン独自のレンダリングを実行するか、専用のレンダー ターゲット(テクスチャ)をターゲットとするまったく別のレンダー パスを生成し、シーン内のアイテムにテクスチャを表示させることです。 ベースのアプローチは、追加のレンダーパスやレンダーターゲットが関与しないという意味で、前者に似ており、 シーン独自のレンダリングと「インライン」でカスタムレンダリングコマンドを注入することができます。Qt Quick before after Qt Quick QSGRenderNode Qt Quick

これら3つのアプローチについては、以下の例を参照してください:

  • Scene Graph - RHI Under QML-QQuickWindow::beforeRendering() シグナルに基づく「アンダーレイアプローチ」を示しています。追加のレンダーパスとリソースは必要ありませんが、Qt Quick シーンの残りの部分との合成とブレンドはかなり制限されます。Qt Quick シーンの「下」または「上」にレンダリングするのが、最も単純なアプローチです。
  • Scene Graph - RHI Texture Item- テクスチャにレンダリングし、生成されたコンテンツでテクスチャ化された四角形を表示するカスタムQQuickItem を作成するデモです。これは非常に柔軟で、Qt Quick シーンの残りの部分と、結果の 2D イメージの完全なブレンドと合成を可能にします。これは、追加のレンダーパスとレンダーターゲットを犠牲にします。
  • この例では、Qt Quick シーングラフがメインレンダーパスの間にカスタムアイテムとノードの実装を呼び出す、「インライン」アプローチを示しています。この方法は、パフォーマンスには優れていますが(余分なレンダリングパス、テクスチャリング、ブレンディングが不要)、潜在的な落とし穴があり、最も複雑な方法です。

カスタムアイテムはQQuickItem から派生します。最も重要なのは、updatePaintNode ()を再実装していることです。

class CustomRender : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QList<QVector2D> vertices READ vertices WRITE setVertices NOTIFY verticesChanged)
    QML_ELEMENT

public:
    explicit CustomRender(QQuickItem *parent = nullptr);

    QList<QVector2D> vertices() const;
    void setVertices(const QList<QVector2D> &newVertices);

signals:
    void verticesChanged();

protected:
    QSGNode *updatePaintNode(QSGNode *old, UpdatePaintNodeData *) override;

private:
    QList<QVector2D> m_vertices;
};

コンストラクタはItemHasContents フラグを設定し、これがビジュアル・アイテムであることを示します。

CustomRender::CustomRender(QQuickItem *parent)
    : QQuickItem(parent)
{
    setFlag(ItemHasContents, true);
    connect(this, &CustomRender::verticesChanged, this, &CustomRender::update);
}

updatePaintNode()実装は、カスタム・シーングラフ・ノードのインスタンスを作成します。このアイテムのバッキングQSGNode ツリーは、QSGRenderNode-derived クラスのインスタンスである 1 つのノードで構成されます。Qt Quick のスレッド・レンダリング・モデルが使用されている場合、この関数はメイン・ス レッドがブロックされたレンダリング・スレッドで呼び出されます。そのため、メイン スレッドのデータ(QQuickItems に格納されているデータなど)にアクセスしても安全です。QSGRenderNode サブクラスのインスタンスであるノードは、レンダースレッド上で「生きる」ことになります。

QSGNode *CustomRender::updatePaintNode(QSGNode *old, UpdatePaintNodeData *)
{
    CustomRenderNode *node = static_cast<CustomRenderNode *>(old);

    if (!node)
        node = new CustomRenderNode(window());

    node->setVertices(m_vertices);

    return node;
}

CustomRenderNode クラスはQSGRenderNode から派生し、多くの仮想関数を再実装しています。QRhi 。したがって、デストラクタから、またはスマート・ポインタを介してリソースを解放することは合法的で安全です。QRhi リソース(バッファ、パイプラインなど)を管理するために、スマート・ポインタはこの場合に非常に便利です。

class CustomRenderNode : public QSGRenderNode
{
public:
    CustomRenderNode(QQuickWindow *window);

    void setVertices(const QList<QVector2D> &vertices);

    void prepare() override;
    void render(const RenderState *state) override;
    void releaseResources() override;
    RenderingFlags flags() const override;
    QSGRenderNode::StateFlags changedStates() const override;

protected:
    QQuickWindow *m_window;
    std::unique_ptr<QRhiBuffer> m_vertexBuffer;
    std::unique_ptr<QRhiBuffer> m_uniformBuffer;
    std::unique_ptr<QRhiShaderResourceBindings> m_resourceBindings;
    std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
    QList<QRhiShaderStage> m_shaders;
    bool m_verticesDirty = true;
    QList<QVector2D> m_vertices;
};

お行儀のよいQSGRenderNode サブクラスはまた、releaseResources ()を再実装します。この場合、単純なreset()呼び出しのセットにすることができます。

void CustomRenderNode::releaseResources()
{
    m_vertexBuffer.reset();
    m_uniformBuffer.reset();
    m_pipeline.reset();
    m_resourceBindings.reset();
}

このQSGRenderNode は、QRhi APIを通じて(OpenGL、Vulkan、Metalなどを通じて直接ではなく)レンダリングを実行し、アイテムのトランスフォームを考慮に入れます(実際には2Dレンダリングしか行わないため)。したがって、適切なフラグを指定することで、パフォーマンスが少し向上する可能性があります。

QSGRenderNode::RenderingFlags CustomRenderNode::flags() const
{
    // We are rendering 2D content directly into the scene graph using QRhi, no
    // direct usage of a 3D API. Hence NoExternalRendering. This is a minor
    // optimization.

    // Additionally, the node takes the item transform into account by relying
    // on projectionMatrix() and matrix() (see prepare()) and never rendering at
    // other Z coordinates. Hence DepthAwareRendering. This is a potentially
    // bigger optimization.

    return QSGRenderNode::NoExternalRendering | QSGRenderNode::DepthAwareRendering;
}

prepare()関数とrender()関数は、Qt Quick シーンがレンダリングするたびに呼び出されます。最初の関数は、レンダーパスを準備する(まだ記録しない)ときに呼び出されます。これは通常、バッファ、テクスチャ、グラフィックパイプラインなどのリソースを作成し(まだ作成されていない場合)、それらへのデータのアップロードをエンキューします。

void CustomRenderNode::prepare()
{
    QRhi *rhi = m_window->rhi();
    QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();

    if (m_verticesDirty) {
        m_vertexBuffer.reset();
        m_verticesDirty = false;
    }

    if (!m_vertexBuffer) {
        m_vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
                                            m_vertices.count() * sizeof(QVector2D)));
        m_vertexBuffer->create();
        resourceUpdates->uploadStaticBuffer(m_vertexBuffer.get(), m_vertices.constData());
    }

render()関数は、QQuickWindow's swapchain、またはテクスチャ(レイヤーアイテムの場合、またはShaderEffectSource 内の場合)のいずれかをターゲットとするレンダーパスの記録がアクティブな間に呼び出されます。

void CustomRenderNode::render(const RenderState *)
{
    QRhiCommandBuffer *cb = commandBuffer();
    cb->setGraphicsPipeline(m_pipeline.get());
    QSize renderTargetSize = renderTarget()->pixelSize();
    cb->setViewport(QRhiViewport(0, 0, renderTargetSize.width(), renderTargetSize.height()));
    cb->setShaderResources();
    QRhiCommandBuffer::VertexInput vertexBindings[] = { { m_vertexBuffer.get(), 0 } };
    cb->setVertexInput(0, 1, vertexBindings);
    cb->draw(m_vertices.count());
}

プロジェクト例 @ code.qt.io

QSGRenderNode,QRhi,Scene Graph - RHI Under QML,Scene Graph - RHI Texture Item,Qt Quick Scene Graphも参照

© 2025 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.