씬 그래프 - 커스텀 지오메트리
Qt Quick 씬 그래프에서 커스텀 지오메트리를 구현하는 방법을 보여줍니다.
커스텀 지오메트리 예제에서는 씬 그래프 API를 사용하여 씬 그래프용 커스텀 지오메트리를 빌드하는 QQuickItem 을 만드는 방법을 보여줍니다. 이를 위해 CustomGeometry 모듈의 일부인 BezierCurve
항목을 생성하고 QML 파일에서 이를 사용합니다.
베지어커브 선언
#include <QtQuick/QQuickItem> class BezierCurve : public QQuickItem { Q_OBJECT Q_PROPERTY(QPointF p1 READ p1 WRITE setP1 NOTIFY p1Changed) Q_PROPERTY(QPointF p2 READ p2 WRITE setP2 NOTIFY p2Changed) Q_PROPERTY(QPointF p3 READ p3 WRITE setP3 NOTIFY p3Changed) Q_PROPERTY(QPointF p4 READ p4 WRITE setP4 NOTIFY p4Changed) Q_PROPERTY(int segmentCount READ segmentCount WRITE setSegmentCount NOTIFY segmentCountChanged) QML_ELEMENT public: BezierCurve(QQuickItem *parent = nullptr); ~BezierCurve(); QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override; QPointF p1() const { return m_p1; } QPointF p2() const { return m_p2; } QPointF p3() const { return m_p3; } QPointF p4() const { return m_p4; } int segmentCount() const { return m_segmentCount; } void setP1(const QPointF &p); void setP2(const QPointF &p); void setP3(const QPointF &p); void setP4(const QPointF &p); void setSegmentCount(int count); signals: void p1Changed(const QPointF &p); void p2Changed(const QPointF &p); void p3Changed(const QPointF &p); void p4Changed(const QPointF &p); void segmentCountChanged(int count); private: QPointF m_p1; QPointF m_p2; QPointF m_p3; QPointF m_p4; int m_segmentCount; };
이 항목 선언은 QQuickItem 클래스를 서브클래싱하고 5개의 프로퍼티를 추가합니다. 베지어 곡선의 네 제어점 각각에 대해 하나씩, 그리고 곡선이 세분화되는 세그먼트 수를 제어하는 매개변수입니다. 각 프로퍼티에는 해당되는 게터 및 세터 함수가 있습니다. 이러한 프로퍼티는 QML에서 바인딩할 수 있으므로 각 프로퍼티에 대한 알림 신호가 있어 변경 사항이 QML 엔진에 반영되고 그에 따라 사용되는 것이 바람직합니다.
QML 씬과 렌더링 씬 그래프 간의 동기화 지점은 가상 함수 QQuickItem::updatePaintNode()이며, 사용자 지정 씬 그래프 로직이 있는 모든 항목은 이 함수를 구현해야 합니다.
참고: 많은 하드웨어 구성에서 씬 그래프는 별도의 스레드에서 렌더링됩니다. 따라서 무엇보다도 QQuickItem::updatePaintNode() 함수를 통해 장면 그래프와의 상호 작용이 제어된 방식으로 이루어지는 것이 중요합니다.
베지어커브 구현
BezierCurve::BezierCurve(QQuickItem *parent) : QQuickItem(parent) , m_p1(0, 0) , m_p2(1, 0) , m_p3(0, 1) , m_p4(1, 1) , m_segmentCount(32) { setFlag(ItemHasContents, true); }
베지어 커브 생성자는 제어점과 세그먼트 수에 대한 기본값을 설정합니다. 베지어 곡선은 항목의 경계 직사각형을 기준으로 정규화된 좌표로 지정됩니다.
생성자는 또한 QQuickItem::ItemHasContents 플래그를 설정합니다. 이 플래그는 캔버스에 이 항목이 시각적 콘텐츠를 제공한다는 것을 알려주며, QML 장면이 렌더링 장면 그래프와 동기화될 때가 되면 QQuickItem::updatePaintNode()를 호출합니다.
BezierCurve::~BezierCurve() = default;
베지어커브 클래스에는 정리해야 할 데이터 멤버가 없으므로 소멸자는 아무 작업도 하지 않습니다. 렌더링 씬 그래프는 다른 스레드에서 씬 그래프 자체에 의해 관리되므로 QQuickItem 클래스에서 QSGNode 참조를 유지하거나 명시적으로 정리하려고 해서는 안 된다는 점을 언급할 필요가 있습니다.
void BezierCurve::setP1(const QPointF &p) { if (p == m_p1) return; m_p1 = p; emit p1Changed(p); update(); }
p1 프로퍼티의 세터 함수는 값이 변경되지 않았는지 확인하고 변경된 경우 조기 종료합니다. 그런 다음 내부 값을 업데이트하고 변경된 신호를 내보냅니다. 그런 다음 QQuickItem::update() 함수를 호출하여 이 객체의 상태가 변경되었으므로 렌더링 장면 그래프와 동기화해야 함을 렌더링 장면 그래프에 알립니다. update()를 호출하면 나중에 QQuickItem::updatePaintNode()를 호출하게 됩니다.
다른 프로퍼티 세터도 이와 동일하므로 이 예제에서는 생략합니다.
QSGNode *BezierCurve::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { QSGGeometryNode *node = nullptr; QSGGeometry *geometry = nullptr; if (!oldNode) { node = new QSGGeometryNode;
updatePaintNode() 함수는 QML 씬의 상태를 렌더링 씬 그래프와 동기화하기 위한 주요 통합 지점입니다. 이 함수는 함수에 대한 마지막 호출에서 반환된 인스턴스인 QSGNode 를 전달받습니다. 함수가 처음 호출될 때는 null이 되고 지오메트리와 머티리얼로 채울 QSGGeometryNode 을 생성합니다.
geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), m_segmentCount); geometry->setLineWidth(2); geometry->setDrawingMode(QSGGeometry::DrawLineStrip); node->setGeometry(geometry); node->setFlag(QSGNode::OwnsGeometry);
그런 다음 지오메트리를 생성하고 노드에 추가합니다. QSGGeometry 생성자의 첫 번째 인수는 "속성 세트"라고 하는 버텍스 유형의 정의입니다. QML에서 자주 사용되는 그래픽은 몇 가지 일반적인 표준 속성 집합을 중심으로 이루어지기 때문에 기본적으로 이러한 속성 집합이 제공됩니다. 여기서는 x 좌표와 y 좌표에 대해 각각 하나씩 두 개의 부동 소수점이 있는 Point2D 속성 집합을 사용합니다. 두 번째 인수는 버텍스 수입니다.
사용자 정의 속성 집합도 만들 수 있지만 이 예제에서는 다루지 않습니다.
지오메트리를 관리하는 데 특별한 메모리가 필요하지 않으므로 QSGGeometryNode 이 지오메트리를 소유하도록 지정합니다.
할당을 최소화하고 메모리 조각화를 줄이며 성능을 개선하기 위해 지오메트리를 QSGGeometryNode 서브클래스의 멤버로 만들 수도 있는데, 이 경우 QSGGeometryNode::OwnsGeometry 플래그를 설정하지 않았을 것입니다.
auto *material = new QSGFlatColorMaterial; material->setColor(QColor(255, 0, 0)); node->setMaterial(material); node->setFlag(QSGNode::OwnsMaterial);
씬 그래프 API는 일반적으로 사용되는 몇 가지 머티리얼 구현을 제공합니다. 이 예제에서는 지오메트리로 정의된 모양을 단색으로 채우는 QSGFlatColorMaterial 을 사용합니다. 여기서도 머티리얼의 소유권을 노드에 전달하여 씬 그래프에서 정리할 수 있도록 합니다.
} else { node = static_cast<QSGGeometryNode *>(oldNode); geometry = node->geometry(); geometry->allocate(m_segmentCount); }
QML 항목이 변경되어 기존 노드의 지오메트리만 수정하려는 경우 oldNode
을 QSGGeometryNode 인스턴스로 캐스팅하고 해당 지오메트리를 추출합니다. 세그먼트 수가 변경된 경우 QSGGeometry::allocate()를 호출하여 올바른 수의 버텍스가 있는지 확인합니다.
QSizeF itemSize = size(); QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D(); for (int i = 0; i < m_segmentCount; ++i) { qreal t = i / qreal(m_segmentCount - 1); qreal invt = 1 - t; QPointF pos = invt * invt * invt * m_p1 + 3 * invt * invt * t * m_p2 + 3 * invt * t * t * m_p3 + t * t * t * m_p4; float x = pos.x() * itemSize.width(); float y = pos.y() * itemSize.height(); vertices[i].set(x, y); } node->markDirty(QSGNode::DirtyGeometry);
지오메트리를 채우기 위해 먼저 지오메트리에서 정점 배열을 추출합니다. 기본 속성 세트 중 하나를 사용하고 있으므로 편의 함수 QSGGeometry::vertexDataAsPoint2D()를 사용할 수 있습니다. 그런 다음 각 세그먼트를 살펴보고 위치를 계산하여 해당 값을 정점에 씁니다.
return node;
}
함수가 끝나면 씬 그래프가 렌더링할 수 있도록 노드를 반환합니다.
애플리케이션 진입점
int main(int argc, char **argv) { QGuiApplication app(argc, argv); QQuickView view; QSurfaceFormat format = view.format(); format.setSamples(16); view.setFormat(format); view.setSource(QUrl("qrc:///scenegraph/customgeometry/main.qml")); view.show(); return app.exec(); }
이 애플리케이션은 간단한 QML 애플리케이션으로, QGuiApplication 및 QQuickView 에서 .qml 파일을 전달합니다.
QML_ELEMENT
BezierCurve 항목을 사용하려면 QML_ELEMENT 매크로를 사용하여 QML 엔진에 등록해야 합니다. 그러면 프로젝트의 빌드 파일에 정의된 대로 BezierCurve라는 이름이 지정되고 CustomGeometry 1.0
모듈의 일부가 됩니다:
# Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(customgeometry_declarative LANGUAGES CXX) find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick) qt_standard_project_setup() qt_add_executable(customgeometry_declarative WIN32 MACOSX_BUNDLE beziercurve.cpp beziercurve.h main.cpp ) target_link_libraries(customgeometry_declarative PRIVATE Qt6::Core Qt6::Gui Qt6::Quick ) qt_add_qml_module(customgeometry_declarative URI CustomGeometry QML_FILES main.qml RESOURCE_PREFIX /scenegraph/customgeometry NO_RESOURCE_TARGET_PATH ) install(TARGETS customgeometry_declarative BUNDLE DESTINATION . RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) qt_generate_deploy_qml_app_script( TARGET customgeometry_declarative OUTPUT_SCRIPT deploy_script MACOS_BUNDLE_POST_BUILD NO_UNSUPPORTED_PLATFORM_ERROR DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM ) install(SCRIPT ${deploy_script})
TARGET = customgeometry QT += quick CONFIG += qmltypes QML_IMPORT_NAME = CustomGeometry QML_IMPORT_MAJOR_VERSION = 1 SOURCES += \ main.cpp \ beziercurve.cpp HEADERS += \ beziercurve.h RESOURCES += customgeometry.qrc target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/customgeometry INSTALLS += target
베지어 커브가 선 스트립으로 그려지기 때문에 앤티앨리어싱을 위해 뷰를 다중 샘플링하도록 지정합니다. 반드시 필요한 것은 아니지만 이를 지원하는 하드웨어에서 항목이 좀 더 멋지게 보이도록 합니다. 멀티샘플링은 메모리 사용량을 증가시키는 경우가 많으므로 기본적으로 활성화되지 않습니다.
항목 사용
import QtQuick import CustomGeometry
.qml 파일은 QtQuick 2.0
모듈을 가져와서 표준 유형을 가져오고, 새로 만든 BezierCurve 객체가 포함된 자체 CustomGeometry 1.0
모듈도 가져옵니다.
Item { width: 300 height: 200 BezierCurve { id: line anchors.fill: parent anchors.margins: 20
그런 다음 루트 항목과 루트를 채우기 위해 앵커링할 베지어커브 인스턴스를 생성합니다.
property real t SequentialAnimation on t { NumberAnimation { to: 1; duration: 2000; easing.type: Easing.InOutQuad } NumberAnimation { to: 0; duration: 2000; easing.type: Easing.InOutQuad } loops: Animation.Infinite } p2: Qt.point(t, 1 - t) p3: Qt.point(1 - t, t) }
예제를 좀 더 흥미롭게 만들기 위해 애니메이션을 추가하여 커브의 두 제어점을 변경합니다. 끝점은 변경되지 않습니다.
Text { anchors.bottom: line.bottom x: 20 width: parent.width - 40 wrapMode: Text.WordWrap text: qsTr("This curve is a custom scene graph item, implemented using line strips") } }
마지막으로 예제에서 보여주는 내용을 간략하게 설명하는 짧은 텍스트를 오버레이합니다.
© 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.