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 ] }
© 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.