Qt Quick 3D - 간단한 스키닝 예제
Qt Quick 3D 에서 간단한 스키닝 애니메이션을 렌더링하는 방법을 보여줍니다.
일반적으로 대부분의 스킨 애니메이션은 모델링 툴을 사용하여 디자인되며, Quick3D는 Balsam 임포터를 통해 glTF 포맷과 Qt Design Studio. 이 예제는 Quick3D에서 각 프로퍼티가 스킨 애니메이션에 어떻게 사용되는지 보여줍니다.
참고: 이 예제의 모든 데이터는 gfTF-튜토리얼 스킨에서 가져온 것입니다.
스키닝 지오메트리를 만듭니다.
사용자 정의 지오메트리 데이터를 사용하기 위해 위치, 조인트, 가중치 및 인덱스가 있는 지오메트리를 정의합니다.
Q_OBJECT QML_NAMED_ELEMENT(SkinGeometry) Q_PROPERTY(QList<QVector3D> positions READ positions WRITE setPositions NOTIFY positionsChanged) Q_PROPERTY(QList<qint32> joints READ joints WRITE setJoints NOTIFY jointsChanged) Q_PROPERTY(QList<float> weights READ weights WRITE setWeights NOTIFY weightsChanged) Q_PROPERTY(QList<quint32> indexes READ indexes WRITE setIndexes NOTIFY indexesChanged)
각 위치는 버텍스 위치이며 각 버텍스에는 4개의 조인트 인덱스와 해당 가중치가 있습니다.
QML에서 스키닝된 데이터 설정
위치 데이터 및 인덱스
꼭지점이 10개인 삼각형 8개를 그려보겠습니다. 아래 표는 QML 코드와 정점의 시각화를 보여줍니다.
QML 코드 | 시각화 |
---|---|
positions: [ Qt.vector3d(0.0, 0.0, 0.0), // vertex 0 Qt.vector3d(1.0, 0.0, 0.0), // vertex 1 Qt.vector3d(0.0, 0.5, 0.0), // vertex 2 Qt.vector3d(1.0, 0.5, 0.0), // vertex 3 Qt.vector3d(0.0, 1.0, 0.0), // vertex 4 Qt.vector3d(1.0, 1.0, 0.0), // vertex 5 Qt.vector3d(0.0, 1.5, 0.0), // vertex 6 Qt.vector3d(1.0, 1.5, 0.0), // vertex 7 Qt.vector3d(0.0, 2.0, 0.0), // vertex 8 Qt.vector3d(1.0, 2.0, 0.0) // vertex 9 ] indexes: [ 0, 1, 3, // triangle 0 0, 3, 2, // triangle 1 2, 3, 5, // triangle 2 2, 5, 4, // triangle 3 4, 5, 7, // triangle 4 4, 7, 6, // triangle 5 6, 7, 9, // triangle 6 6, 9, 8 // triangle 7 ] |
조인트 및 가중치 데이터
모든 버텍스는 스키닝 프로세스 중에 영향을 미치는 조인트의 인덱스를 지정해야 합니다. 각 버텍스에 대해 이러한 인덱스를 4D 벡터로 저장합니다(Qt는 버텍스에 영향을 줄 수 있는 조인트의 수를 4개로 제한합니다). 지오메트리에는 두 개의 조인트 노드(0과 1)만 있지만 4D 벡터를 사용하므로 나머지 두 개의 조인트 인덱스와 가중치는 0으로 설정합니다.
joints: [ 0, 1, 0, 0, // vertex 0 0, 1, 0, 0, // vertex 1 0, 1, 0, 0, // vertex 2 0, 1, 0, 0, // vertex 3 0, 1, 0, 0, // vertex 4 0, 1, 0, 0, // vertex 5 0, 1, 0, 0, // vertex 6 0, 1, 0, 0, // vertex 7 0, 1, 0, 0, // vertex 8 0, 1, 0, 0 // vertex 9 ]
해당 가중치 값은 아래와 같습니다.
weights: [ 1.00, 0.00, 0.0, 0.0, // vertex 0 1.00, 0.00, 0.0, 0.0, // vertex 1 0.75, 0.25, 0.0, 0.0, // vertex 2 0.75, 0.25, 0.0, 0.0, // vertex 3 0.50, 0.50, 0.0, 0.0, // vertex 4 0.50, 0.50, 0.0, 0.0, // vertex 5 0.25, 0.75, 0.0, 0.0, // vertex 6 0.25, 0.75, 0.0, 0.0, // vertex 7 0.00, 1.00, 0.0, 0.0, // vertex 8 0.00, 1.00, 0.0, 0.0 // vertex 9 ]
스켈레톤 및 조인트 계층 구조
스키닝을 위해 Model 에 스켈레톤 프로퍼티를 추가합니다:
skeleton: qmlskeleton Skeleton { id: qmlskeleton Joint { id: joint0 index: 0 skeletonRoot: qmlskeleton Joint { id: joint1 index: 1 skeletonRoot: qmlskeleton eulerRotation.z: 45 } } }
두 개의 Joint는 Skeleton 로 연결됩니다. joint1
를 z축을 중심으로 45도 회전합니다. 아래 이미지는 지오메트리에 조인트가 배치되는 방식과 초기 스켈레톤의 방향을 보여줍니다.
지오메트리의 조인트 | 초기 스켈레톤 |
---|---|
역바인드포즈를 사용하여 모델 배치하기
모델에 유효한 skeleton 이 있으면 스켈레톤의 초기 포즈를 정의해야 합니다. 이는 스켈레탈 애니메이션의 기준선을 정의합니다. 조인트를 초기 위치에서 이동하면 모델의 버텍스가 joints
및 weights
표에 따라 움직입니다. 각 노드의 지오메트리는 독특한 방식으로 지정됩니다. Model.inverseBindPoses 은 조인트를 초기 위치로 변환하는 행렬의 역으로 설정됩니다. 중앙으로 이동하려면 두 조인트에 대해 동일한 변환, 즉 x축을 따라 -0.5, y축을 따라 -1.0으로 변환하는 행렬을 설정하면 됩니다.
QML 코드 | 초기 위치 | 결과 |
---|---|---|
inverseBindPoses: [ Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1), Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1) ] |
조인트 노드로 애니메이션 적용
이제 스키닝된 오브젝트를 준비했으므로 Joints의 속성, 특히 eulerRotation을 변경하여 애니메이션을 적용할 수 있습니다.
Timeline { id: timeline0 startFrame: 0 endFrame: 1000 currentFrame: 0 enabled: true animations: [ TimelineAnimation { duration: 5000 from: 0 to: 1000 running: true } ] KeyframeGroup { target: joint1 property: "eulerRotation.z" Keyframe { frame: 0 value: 0 } Keyframe { frame: 250 value: 90 } Keyframe { frame: 750 value: -90 } Keyframe { frame: 1000 value: 0 } } }
스키닝에 대한 보다 완벽한 접근 방식
스켈레톤은 리소스이지만 계층 구조와 위치가 모델의 변형에 사용됩니다.
Skeleton 노드 대신 리소스 유형 Skin 을 사용할 수 있습니다. Skin 유형은 씬의 공간 노드가 아니므로 그 위치는 모델에 영향을 미치지 않습니다. 최소한의 작동하는 스킨 노드는 일반적으로 노드 목록, 조인트 및 선택적 역 바인드 매트릭스인 inverseBindPoses로 구성됩니다.
Skin 항목을 사용하면 이전 예제를 다음과 같이 작성할 수 있습니다:
skin: Skin { id: skin0 joints: [ joint0, joint1 ] inverseBindPoses: [ Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1), Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1) ] }
코드 스니펫을 보면 Skin 에는 조인트와 inverseBindPoses라는 두 개의 목록만 있으며, 이는 계층 구조가 없고 기존 노드의 계층 구조만 사용한다는 점에서 Skeleton 접근 방식과 다릅니다.
© 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.