Qt Quick 3D supports vertex skinning for skeletal animation of mesh geometries.
见 Simple Skinning Example for a practical demonstration of skeletal animation.
In most cases, application developers will not be using the skinning API manually. The normal workflow is to use an external content creation tool to define the skeleton and the skin (this is sometimes also referred to as rigging ), and then use the 香脂资产导入工具 to convert the asset to Qt Quick 3D's native format.
The basis of skeletal animation is the Skeleton . This is an abstract representation of how the model can move, inspired by how a physical skeleton works for vertebrates. The "bones" of the skeleton is represented by a hierarchy of Joint nodes. These do not necessarily need to represent actual bones, of course.
Skeleton { id: qmlskeleton Joint { id: joint0 index: 0 skeletonRoot: qmlskeleton Joint { id: joint1 index: 1 skeletonRoot: qmlskeleton } Joint { id: joint2 index: 2 skeletonRoot: qmlskeleton } } }
To apply a skeleton to a model, set the model's skeleton 特性:
Model { skeleton: qmlskeleton ...
In order for the skeleton to have an effect, the model's
geometry
needs to include skinning information. This is done by including
vertex attributes
with
JointSemantic
and
WeightSemantic
in the vertex buffer.
JointSemantic
attribute determines
which
of the joints in the skeleton can influence a given vertex. This uses the index values specified by
Joint.index
. Since this attribute contains 4 indexes, a maximum of 4 joints can influence one vertex.
WeightSemantic
attribute describes the
strength
of the influence of those joints. It contains four floating point values, each value determining the weight given to the joint with the index at the corresponding position in the
JointSemantic
属性。
For example, given the skeleton above, if a vertex has these attributes:
JointSemantic
属性
|
WeightSemantic
属性
|
---|---|
QVector4D(2, 0, 0, 0)
|
QVector4D(1.0, 0.0, 0.0, 0.0)
|
that vertex will be 100% influenced by
joint2
, and it will move exactly as much as that joint. The last three indexes in the
JointSemantic
attribute are ignored since the corresponding weights are
0.0
.
As another example, with these attributes:
JointSemantic
属性
|
WeightSemantic
属性
|
---|---|
QVector4D(1, 2, 0, 0)
|
QVector4D(0.5, 0.25, 0.0, 0.0)
|
the vertex will be moved by 50% of joint1 's movement plus 25% of joint2 's movement.
In addition, since the skeleton is an abstract representation, the model need to specify geometry information for the joints. For performance reasons, this is not done by specifying the information directly. Instead, Model.inverseBindPoses 包含 inverse of the transformation matrix needed to move each joint to its initial position.
Transforming a joint in a skeleton will move all vertexes connected to that joint. Since Joint inheriths from Node , a skeleton can be animated simply by using standard QML animations .
NumberAnimation { target: joint1 property: "eulerRotation.z" duration: 5000 from: -90 to: 90 running: true }
While it is possible to create complex animations by nesting SequentialAnimation , ParallelAnimation and NumberAnimation , it is generally more convenient to use timeline animations for animating skinned models.