Sur cette page

Qt Quick 3D - Exemple d'écorché simple

Démontre comment rendre une simple animation de peau dans Qt Quick 3D.

Deux formes animées montrant la déformation du squelette

En général, la plupart des animations de peau seront conçues par des outils de modélisation, et Quick3D supporte également les formats glTF grâce à l'importateur Balsam et à l'outil Qt Design Studio. Cet exemple montre comment chaque propriété est utilisée pour l'animation de la peau dans Quick3D.

Note : Toutes les données de cet exemple proviennent du gfTF-Tutorial Skins.

Créer une géométrie de skin.

Pour utiliser des données géométriques personnalisées, nous allons définir une géométrie ayant des positions, des articulations, des poids et des index.

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)

Chaque position est une position de sommet et chaque sommet a 4 index d'articulations et les poids correspondants.

Mise en place des données skinnées en QML

Données de position et index

Nous allons dessiner 8 triangles avec 10 sommets. Le tableau ci-dessous présente le code QML et une visualisation des sommets.

Code QMLVisualisé
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
]

Positions des sommets et géomérie

Données sur les articulations et les poids

Chaque sommet doit spécifier les index des articulations qui devraient avoir une influence sur lui au cours du processus d'écorchage. Pour chaque sommet, nous stockons ces index sous forme de vecteurs 4D (Qt limite à 4 le nombre d'articulations pouvant influencer un sommet). Notre géométrie n'aura que deux nœuds d'articulation (0 et 1), mais comme nous utilisons des vecteurs 4D, nous fixons les deux index d'articulation restants et leurs poids à 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
]

Les valeurs de poids correspondantes sont indiquées ci-dessous.

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
]
Squelette et hiérarchie des articulations

Pour l'écorchage, nous ajoutons une propriété de squelette à l'adresse Model:

skeleton: qmlskeleton
Skeleton {
    id: qmlskeleton
    Joint {
        id: joint0
        index: 0
        skeletonRoot: qmlskeleton
        Joint {
            id: joint1
            index: 1
            skeletonRoot: qmlskeleton
            eulerRotation.z: 45
        }
    }
}

Les deux Jointsont reliés dans un Skeleton. Nous ferons pivoter joint1 de 45 degrés autour de l'axe z. Les images ci-dessous montrent comment les articulations sont placées dans la géométrie et comment le squelette initial est orienté.

Articulations dans la géométrieSquelette initial

2 joints dans la géométrie

Squelette initial

Placement des modèles à l'aide d'inverseBindPoses

Une fois qu'un modèle a une skeleton valide, il est nécessaire de définir la pose initiale du squelette. Celle-ci définit la ligne de base de l'animation du squelette : le déplacement d'une articulation à partir de sa position initiale entraîne le déplacement des sommets du modèle selon les tables joints et weights. La géométrie de chaque nœud est spécifiée d'une manière particulière : Model.inverseBindPoses est défini comme l'inverse de la matrice qui transformerait l'articulation à sa position initiale. Pour la déplacer vers le centre, nous allons simplement définir la même transformation pour les deux articulations : une matrice qui traduit -0,5 le long de l'axe x et -1,0 le long de l'axe y.

Code QMLPosition initialeRésultat
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)
]

Position initiale

Transformé par InversebindPoses

Animer avec des nœuds d'articulation

Maintenant que nous avons préparé un objet skinné, nous pouvons l'animer en modifiant les propriétés de Joints, en particulier 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
        }
    }
}

Une approche plus complète du skinning

Le squelette est une ressource, mais sa hiérarchie et sa position sont utilisées pour la transformation du modèle.

Au lieu d'un nœud Skeleton, nous pouvons utiliser le type de ressource Skin. Comme le type Skin n'est pas un nœud spatial dans la scène, sa position n'affectera pas le modèle. Un nœud Skin fonctionnel minimal se compose normalement d'une liste de nœuds, d'articulations et d'une matrice de liaison inverse facultative, inverseBindPoses.

En utilisant l'élément Skin, l'exemple précédent peut être écrit comme suit :

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)
    ]
}

L'extrait de code montre que Skin n'a que deux listes, une liste d'articulations et une liste de matrices de liaison inverse, ce qui diffère de l'approche de Skeleton, qui n'a pas de hiérarchie et utilise simplement la hiérarchie des nœuds existants.

Node {
    id: joint0
    Node {
        id: joint1
        eulerRotation.z: 45
    }
}

Exemple de projet @ code.qt.io

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