Qt Quick 3D - 사용자 정의 모핑 애니메이션
모프 타깃을 사용하여 C++ 사용자 정의 지오메트리를 작성하는 방법을 보여줍니다.

이 예제는 기본 도형과 모프 타겟을 포함하며, 두 개체 모두에 법선 벡터가 지정된 복잡한 사용자 정의 지오메트리를 C++로 정의하는 방법을 보여줍니다.
사용자 정의 지오메트리
이 예제의 핵심은 모프 타겟을 가진 사용자 정의 지오메트리를 생성하는 것입니다. 이를 위해 ` QQuick3DGeometry`을 상속받아 구현합니다:
class MorphGeometry : public QQuick3DGeometry
{
Q_OBJECT
QML_NAMED_ELEMENT(MorphGeometry)
Q_PROPERTY(int gridSize READ gridSize WRITE setGridSize NOTIFY gridSizeChanged)
public:
MorphGeometry(QQuick3DObject *parent = nullptr);
int gridSize() { return m_gridSize; }
void setGridSize(int gridSize);
signals:
void gridSizeChanged();
private:
void calculateGeometry();
void updateData();
QList<QVector3D> m_positions;
QList<QVector3D> m_normals;
QList<QVector4D> m_colors;
QList<QVector3D> m_targetPositions;
QList<QVector3D> m_targetNormals;
QList<QVector4D> m_targetColors;
QList<quint32> m_indexes;
QByteArray m_vertexBuffer;
QByteArray m_indexBuffer;
QByteArray m_targetBuffer;
int m_gridSize = 50;
QVector3D boundsMin;
QVector3D boundsMax;
};생성자는 메시 데이터의 레이아웃을 정의합니다:
MorphGeometry::MorphGeometry(QQuick3DObject *parent)
: QQuick3DGeometry(parent)
{
updateData();
}updateData 함수는 메쉬 지오메트리의 실제 업로드를 수행합니다:
void MorphGeometry::updateData()
{
clear();
calculateGeometry();
addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
QQuick3DGeometry::Attribute::ComponentType::F32Type);
addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 3 * sizeof(float),
QQuick3DGeometry::Attribute::ComponentType::F32Type);
addAttribute(QQuick3DGeometry::Attribute::ColorSemantic, 6 * sizeof(float),
QQuick3DGeometry::Attribute::ComponentType::F32Type);
addTargetAttribute(0, QQuick3DGeometry::Attribute::PositionSemantic, 0);
addTargetAttribute(0, QQuick3DGeometry::Attribute::NormalSemantic, m_targetPositions.size() * sizeof(float) * 3);
addTargetAttribute(0, QQuick3DGeometry::Attribute::ColorSemantic,
m_targetPositions.size() * sizeof(float) * 3 + m_targetNormals.size() * sizeof(float) * 3);
addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0,
QQuick3DGeometry::Attribute::ComponentType::U32Type);
const int numVertexes = m_positions.size();
m_vertexBuffer.resize(numVertexes * sizeof(Vertex));
Vertex *vert = reinterpret_cast<Vertex *>(m_vertexBuffer.data());
for (int i = 0; i < numVertexes; ++i) {
Vertex &v = vert[i];
v.position = m_positions[i];
v.normal = m_normals[i];
v.color = m_colors[i];
}
m_targetBuffer.append(QByteArray(reinterpret_cast<char *>(m_targetPositions.data()), m_targetPositions.size() * sizeof(QVector3D)));
m_targetBuffer.append(QByteArray(reinterpret_cast<char *>(m_targetNormals.data()), m_targetNormals.size() * sizeof(QVector3D)));
m_targetBuffer.append(QByteArray(reinterpret_cast<char *>(m_targetColors.data()), m_targetColors.size() * sizeof(QVector4D)));
setStride(sizeof(Vertex));
setVertexData(m_vertexBuffer);
setTargetData(m_targetBuffer);
setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
setBounds(boundsMin, boundsMax);
m_indexBuffer = QByteArray(reinterpret_cast<char *>(m_indexes.data()), m_indexes.size() * sizeof(quint32));
setIndexData(m_indexBuffer);
}우리는 생성자에서 그리고 속성이 변경되었을 때 ` updateData `를 호출합니다.
calculateGeometry 함수에는 형상과 법선 벡터를 계산하는 복잡한 수학적 계산이 모두 포함되어 있습니다. 이는 이 예제에 특화된 내용이며, 코드에 대한 자세한 설명은 생략하겠습니다. 일반적으로 부드러운 셰이딩을 구현하려면 각 정점에 대한 법선 벡터를 계산해야 합니다. 수학적으로 법선 벡터는 평면을 묘사하는 함수의 편미분으로부터 계산할 수 있습니다:

이 예제에서는 기본 도형으로 코사인 파형을 사용하여, 그 도함수가 사인 함수라는 점을 활용해 계산 과정을 단순화합니다.
실제로는 기하학적 추론을 통해 법선 벡터를 결정할 수 있는 경우가 많습니다. 모프 타깃의 경우, 구의 중심에서 표면으로 향하는 모든 벡터는 해당 지점에서 구에 수직이 된다는 사실을 활용합니다. QtQuick3D 에서 법선 벡터는 단위 길이를 가져야 하며, 이는 ` QVector3D::normalized()`를 사용하여 구현할 수 있습니다.
QML 부분
사용자 정의 기하 구조에서 생성한 것과 일치하는 모프 타깃을 정의하고, 가중치에 애니메이션을 적용하여 두 모양 사이를 순환하도록 합니다:
MorphTarget {
id: morphtarget
attributes: MorphTarget.Position | MorphTarget.Normal | MorphTarget.Color
SequentialAnimation on weight {
PauseAnimation { duration: 1000 }
NumberAnimation { from: 0; to: 1; duration: 4000 }
PauseAnimation { duration: 1000 }
NumberAnimation { from: 1; to: 0; duration: 4000 }
loops: Animation.Infinite
}
}마지막으로, 사용자 정의 지오메트리를 사용하여 모델을 생성하고, 해당 모델에 모프 타겟을 적용합니다:
Model {
y: -1
geometry: MorphGeometry {}
morphTargets: [ morphtarget ]
materials: [ material ]
}© 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.