En esta página

Qt Quick 3D - Ejemplo de skinning simple

Demuestra cómo renderizar una animación simple de skinning en Qt Quick 3D.

Dos formas animadas que muestran la deformación del esqueleto

Generalmente la mayoría de las animaciones de skin serán diseñadas por herramientas de modelado, y Quick3D también soporta formatos glTF a través del importador Balsam y Qt Design Studio. Este ejemplo muestra cómo se utiliza cada propiedad para la animación de skin en Quick3D.

Nota: Todos los datos de este ejemplo provienen de gfTF-Tutorial Skins.

Haga una geometría de skinning.

Para utilizar datos de geometría personalizados, definiremos una geometría que tenga posiciones, articulaciones, pesos e índices.

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)

Cada posición es una posición de vértice y cada vértice tiene 4 índices de articulaciones y sus pesos correspondientes.

Configurar datos personalizados en QML

Datos de posición e índices

Dibujaremos 8 triángulos con 10 vértices. La siguiente tabla muestra el código QML y una visualización de los vértices.

Código QMLVisualización
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
]

Posiciones de vértice y geomería

Datos de posición y pesos

Cada vértice necesita especificar los índices de las articulaciones que deben influir sobre él durante el proceso de despellejado. Para cada vértice almacenamos estos índices como vectores 4D (Qt limita el número de articulaciones que pueden influir en un vértice a 4). Nuestra geometría tendrá sólo dos nodos de articulación (0 y 1), pero como usamos vectores 4D establecemos los dos índices de articulación restantes y sus pesos a 0. Los valores de peso correspondientes son los siguientes

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
]

Los valores de peso correspondientes son los siguientes.

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
]
Esqueleto y jerarquía de articulaciones

Para el skinning, añadimos una propiedad de esqueleto a Model:

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

Los dos Joints están conectados en un Skeleton. Rotaremos joint1 45 grados alrededor del eje z. Las imágenes siguientes muestran cómo se colocan las articulaciones en la geometría y cómo se orienta el esqueleto inicial.

Articulaciones en la geometríaEsqueleto inicial

2 juntas en la geometría

Esqueleto inicial

Colocación de modelos mediante inverseBindPoses

Una vez que un modelo tiene un skeleton válido, es necesario definir la pose inicial del esqueleto. Esto define la línea base para la animación del esqueleto: mover una articulación desde su posición inicial hace que los vértices del modelo se muevan de acuerdo con las tablas joints y weights. La geometría de cada nodo se especifica de una manera peculiar: Model.inverseBindPoses se establece a la inversa de la matriz que transformaría la articulación a su posición inicial. Para moverla al centro, simplemente estableceremos la misma transformación para ambas articulaciones: una matriz que traslada -0.5 a lo largo del eje x y -1.0 a lo largo del eje y.

Código QMLPosición inicialResultado
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)
]

Posición inicial

Transformado por InversebindPoses

Animar con nodos Joint

Ahora que hemos preparado un objeto con skin, podemos animarlo cambiando las propiedades de Joint, específicamente 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
        }
    }
}

Una aproximación más completa al skinning

El esqueleto es un recurso, pero su jerarquía y posición se utiliza para la transformación del modelo.

En lugar de un nodo Skeleton, podemos utilizar el tipo de recurso Skin. Dado que el tipo Skin no es un nodo espacial en la escena, su posición no afectará al modelo. Un nodo Skin de trabajo mínimo consistirá normalmente en una lista de nodos, articulaciones y una matriz de bind inversa opcional, inverseBindPoses.

Usando el elemento Skin el ejemplo anterior puede escribirse así:

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

Del fragmento de código podemos ver que el Skin sólo tiene dos listas, una joints y una inverseBindPoses, lo que difiere del enfoque Skeleton, ya que no tiene ninguna jerarquía y sólo utiliza la jerarquía del nodo existente.

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

Proyecto de ejemplo @ 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.