씬 그래프 - 커스텀 QSGRenderNode
Qt Quick 장면 그래프에서 커스텀 렌더링을 구현하기 위해 QSGRenderNode 을 사용하는 방법을 보여줍니다.
커스텀 렌더 노드 예제는 QSGRenderNode 에서 파생된 씬 그래프 노드로 지원되는 QQuickItem 서브클래스를 구현하여 자체적인 QRhi 기반 렌더링을 제공하는 방법을 보여줍니다.
참고: 이 예제는 Qt GUI 모듈의 호환성이 제한적으로 보장되는 API에 의존하면서 이식 가능한 크로스 플랫폼 3D 렌더링을 수행하는 고급 저수준 기능을 보여줍니다. QRhi API를 사용하기 위해 애플리케이션은 Qt::GuiPrivate
으로 연결되며 <rhi/qrhi.h>
.
QSGRenderNode 를 사용하면 시나리오 내에서 렌더 하드웨어 인터페이스(RHI)에 직접 액세스할 수 있습니다. 이 예시에서는 QSGRenderNode 기반 렌더 노드를 만들고 사용자 지정 항목으로 관리하는 방법을 보여줍니다. 렌더 노드는 RHI 파이프라인을 생성하고 버텍스 및 유니폼 버퍼를 업데이트하며 RHI 명령 버퍼로 렌더링합니다.
실제로 이 방식은 OpenGL, Metal 또는 Vulkan과 같은 네이티브 3D API를 사용하지 않고 시나리오 자체의 렌더링과 인라인으로 커스텀 렌더링을 수행하는 이식 가능한 크로스 플랫폼 접근 방식입니다. 대신 이 애플리케이션은 Qt의 그래픽 및 셰이더 추상화 레이어를 사용합니다.
QSGRenderNode 는 사용자 지정 2D/3D 렌더링을 Qt Quick 장면에 통합하는 세 가지 방법 중 하나를 지원합니다. 다른 두 가지 옵션은 before
또는 after
장면의 자체 렌더링을 수행하거나 Qt Quick 장면의 전용 렌더링 대상(텍스처)을 대상으로 별도의 렌더링 패스를 생성한 다음 장면의 항목이 텍스처를 표시하도록 하는 것입니다. QSGRenderNode 기반 접근 방식은 추가 렌더 패스나 렌더 타깃이 포함되지 않는다는 점에서 전자와 유사하며, Qt Quick 씬의 자체 렌더링에 사용자 지정 렌더링 명령을 "인라인"으로 삽입할 수 있습니다.
이 세 가지 접근 방식에 대해서는 다음 예제를 참조하세요:
- 씬 그래프 - QML 아래의 RHI - QQuickWindow::beforeRendering() 신호를 기반으로 하는 "언더레이" 접근 방식을 보여줍니다. 추가 렌더 패스 및 리소스가 필요하지 않지만 나머지 Qt Quick 씬과의 구성 및 블렌딩은 상당히 제한적입니다. Qt Quick 씬의 "아래" 또는 "위"에 렌더링하는 것이 가장 간단한 접근 방식입니다.
- 씬 그래프 - RHI 텍스처 항목 - 텍스처로 렌더링하고 생성된 콘텐츠로 쿼드 텍스처를 표시하는 사용자 지정 QQuickItem 만들기를 시연합니다. 이 방법은 매우 유연하며 결과 2D 이미지를 나머지 Qt Quick 장면과 완벽하게 혼합하고 구성할 수 있습니다. 대신 렌더 패스 및 렌더 타겟이 추가로 필요합니다.
- 이 예시 - 메인 렌더 패스 중에 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 파생 클래스의 인스턴스로 구성됩니다. 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 는 OpenGL, Vulkan, Metal 등을 통해 직접 렌더링하는 것이 아니라 QRhi API를 통해 렌더링을 수행하며, 아이템 트랜스폼을 고려합니다(실제로는 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; }
준비() 및 렌더() 함수는 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 의 스왑체인 또는 텍스처(레이어드 아이템의 경우 또는 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()); }
QSGRenderNode, QRhi, 씬 그래프 - QML 아래의 RHI, 씬 그래프 - RHI 텍스처 항목 및 Qt Quick 씬 그래프도참조하세요 .
© 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.