Qt Quick 3D - Einfaches Skinning-Beispiel
Zeigt, wie man eine einfache Skinning-Animation in Qt Quick 3D rendert.
Im Allgemeinen werden die meisten Hautanimationen mit Modellierungswerkzeugen erstellt, und Quick3D unterstützt auch glTF-Formate durch den Balsam-Importer und Qt Design Studio. Dieses Beispiel zeigt, wie jede Eigenschaft für die Hautanimation in Quick3D verwendet wird.
Hinweis: Alle Daten in diesem Beispiel stammen aus dem gfTF-Tutorial Skins.
Erstellen Sie eine Skinning-Geometrie.
Um benutzerdefinierte Geometriedaten zu verwenden, werden wir eine Geometrie mit Positionen, Gelenken, Gewichten und Indizes definieren.
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)
Jede Position ist eine Scheitelpunktposition und jeder Scheitelpunkt hat 4 Gelenkindizes und entsprechende Gewichte.
Einrichten von Skinned-Daten in QML
Positionsdaten und Indizes
Wir werden 8 Dreiecke mit 10 Scheitelpunkten zeichnen. Die folgende Tabelle zeigt den QML-Code und eine Visualisierung der Scheitelpunkte.
QML-Code | Visualisierte |
---|---|
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 ] |
Daten zu Knotenpunkten und Gewichten
Für jeden Scheitelpunkt müssen die Indizes der Gelenke angegeben werden, die während des Häutungsprozesses einen Einfluss auf ihn haben sollen. Für jeden Vertex speichern wir diese Indizes als 4D-Vektoren (Qt begrenzt die Anzahl der Gelenke, die einen Vertex beeinflussen können, auf 4). Unsere Geometrie wird nur zwei Gelenkknoten haben (0 und 1), aber da wir 4D-Vektoren verwenden, setzen wir die verbleibenden zwei Gelenkindizes und ihre Gewichte auf 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 ]
Die entsprechenden Gewichtungswerte lauten wie unten.
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 ]
Skelett und Gelenkhierarchie
Für das Skinning fügen wir der Model eine Skeletteigenschaft hinzu:
skeleton: qmlskeleton Skeleton { id: qmlskeleton Joint { id: joint0 index: 0 skeletonRoot: qmlskeleton Joint { id: joint1 index: 1 skeletonRoot: qmlskeleton eulerRotation.z: 45 } } }
Die beiden Jointsind in einem Skeleton verbunden. Wir werden joint1
um 45 Grad um die z-Achse drehen. Die folgenden Bilder zeigen, wie die Gelenke in der Geometrie platziert werden und wie das ursprüngliche Skelett ausgerichtet ist.
Gelenke in der Geometrie | Ursprüngliches Skelett |
---|---|
Platzieren von Modellen mit inverseBindPoses
Sobald ein Modell eine gültige skeleton hat, muss die Ausgangsposition des Skeletts definiert werden. Damit wird die Grundlinie für die Skelettanimation festgelegt: Das Bewegen eines Gelenks aus seiner Ausgangsposition führt dazu, dass sich die Eckpunkte des Modells gemäß den Tabellen joints
und weights
bewegen. Die Geometrie jedes Knotens wird auf eine besondere Weise spezifiziert: Model.inverseBindPoses wird auf die Inverse der Matrix gesetzt, die das Gelenk in seine Ausgangsposition transformieren würde. Um es in die Mitte zu verschieben, wird einfach dieselbe Transformation für beide Gelenke festgelegt: eine Matrix, die -0,5 entlang der x-Achse und -1,0 entlang der y-Achse verschiebt.
QML-Code | Ausgangsposition | Ergebnis |
---|---|---|
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) ] |
Animieren mit Gelenkknoten
Nachdem wir nun ein gehäutetes Objekt vorbereitet haben, können wir es animieren, indem wir die Eigenschaften von Jointändern, insbesondere die 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 } } }
Ein vollständigerer Ansatz für das Skinning
Das Skelett ist eine Ressource, aber seine Hierarchie und Position wird für die Transformation des Modells verwendet.
Anstelle eines Skeleton Knotens können wir den Ressourcentyp Skin verwenden. Da der Typ Skin kein räumlicher Knoten in der Szene ist, wird seine Position das Modell nicht beeinflussen. Ein minimaler Arbeits-Skin-Knoten besteht normalerweise aus einer Knotenliste, Gelenken und einer optionalen inversen Bindungsmatrix, inverseBindPoses.
Unter Verwendung des Skin Elements kann das vorherige Beispiel wie folgt geschrieben werden:
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) ] }
Aus dem Codeschnipsel können wir ersehen, dass Skin nur zwei Listen hat, eine Joints und eine inverseBindPoses, was sich von dem Skeleton Ansatz unterscheidet, da es keine Hierarchie hat und einfach die bestehende Knotenhierarchie verwendet.
© 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.