脖子不正常---骨骼动画

neck is abnormal---skeletal animation

库:assimp;模型:*.fbx

骨骼动画可以正常播放了。 但是角色的脖子是伸着的,一动不动

使用renderdoc发现输入vertex shader的顶点是没有问题的。但是输出顶点显示颈部异常。 我还是不知道问题出在哪里

角色的头不能动,脖子被拉长了。 我想知道到底哪里错了?有什么问题可以让头动不了?

这是错误发生时的结果:

将输入传递给着色器

for (size_t i = 0; i < MAX_BONES; ++i){
    g_Bones.bones[i] = glm::mat4(1.0f);//为了让没有骨头的模型正常显示
}
for (int32_t i = 0; i < g_Model->meshes.size(); ++i) {
    DVKMesh* mesh = g_Model->meshes[i];
    g_Position.model = mesh->linkNode->GetGlobalMatrix();
    for (int32_t j = 0; j < mesh->bones.size(); j++) {
        DVKBone* bone = g_Model->bones[mesh->bones[j]]; 
        g_Bones.bones[j] = bone->finalTransform;
        g_Bones.bones[j] = glm::inverse(mesh->linkNode->GetGlobalMatrix()) * g_Bones.bones[j];
    }
    if (mesh->bones.empty()) {
        g_Bones.bones[mesh->bones.size()] = glm::mat4(1.0f);
    }
    bufferData(g_VulkanBasic.device, sizeof(Position_UBO), &g_Position, g_PositionBuffer.memory, i * g_MinUniformBufferOffset);
}
bufferData(g_VulkanBasic.device, sizeof(BonesTransformBlock), g_Bones.bones, g_BonesBuffer.memory);

加载骨骼和加载皮肤

void FillMatrixWithAiMatrix(glm::mat4& matrix, const aiMatrix4x4& aiMatrix) {
    matrix[0][0] = aiMatrix.a1;
    matrix[0][1] = aiMatrix.a2;
    matrix[0][2] = aiMatrix.a3;
    matrix[0][3] = aiMatrix.a4;
    matrix[1][0] = aiMatrix.b1;
    matrix[1][1] = aiMatrix.b2;
    matrix[1][2] = aiMatrix.b3;
    matrix[1][3] = aiMatrix.b4;
    matrix[2][0] = aiMatrix.c1;
    matrix[2][1] = aiMatrix.c2;
    matrix[2][2] = aiMatrix.c3;
    matrix[2][3] = aiMatrix.c4;
    matrix[3][0] = aiMatrix.d1;
    matrix[3][1] = aiMatrix.d2;
    matrix[3][2] = aiMatrix.d3;
    matrix[3][3] = aiMatrix.d4;
    matrix = glm::transpose(matrix);
}
void DVKModel::LoadBones(const aiScene* aiScene){
    std::unordered_map<std::string, int32_t> boneIndexMap;
    for (int32_t i = 0; i < (int32_t)aiScene->mNumMeshes; ++i){
        aiMesh* aimesh = aiScene->mMeshes[i];
        for (int32_t j = 0; j < (int32_t)aimesh->mNumBones; ++j){
            aiBone* aibone = aimesh->mBones[j];
            std::string name = aibone->mName.C_Str();
            auto it = boneIndexMap.find(name);
            if (it == boneIndexMap.end()){
                // new bone
                int32_t index = (int32_t)bones.size();
                DVKBone* bone = new DVKBone();
                bone->index = index;
                bone->parent = -1;
                bone->name = name;
                FillMatrixWithAiMatrix(bone->inverseBindPose, aibone->mOffsetMatrix);
                bones.push_back(bone);
                bonesMap.insert(std::make_pair(name, bone));
                boneIndexMap.insert(std::make_pair(name, index));
            }
        }
    }
}
void DVKModel::LoadSkin(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene){
    std::unordered_map<int32_t, int32_t> boneIndexMap;
    for (int32_t i = 0; i < (int32_t)aiMesh->mNumBones; ++i){
        aiBone* boneInfo = aiMesh->mBones[i];
        std::string boneName(boneInfo->mName.C_Str());
        int32_t boneIndex = bonesMap[boneName]->index;
        // bone在mesh中的索引
        int32_t meshBoneIndex = 0;
        auto it = boneIndexMap.find(boneIndex);
        if (it == boneIndexMap.end()){
            meshBoneIndex = (int32_t)mesh->bones.size();
            mesh->bones.push_back(boneIndex);
            boneIndexMap.insert(std::make_pair(boneIndex, meshBoneIndex));
        }
        else{
            meshBoneIndex = it->second;
        }
        for (uint32_t j = 0; j < boneInfo->mNumWeights; ++j){
            uint32_t vertexID = boneInfo->mWeights[j].mVertexId;
            float  weight = boneInfo->mWeights[j].mWeight;
            // 顶点->Bone
            if (skinInfoMap.find(vertexID) == skinInfoMap.end()){
                skinInfoMap.insert(std::make_pair(vertexID, DVKVertexSkin()));
            }
            DVKVertexSkin* info = &(skinInfoMap[vertexID]);
            info->indices[info->used] = meshBoneIndex;
            info->weights[info->used] = weight;
            ++info->used;
            if (info->used >= 4){
                break;
            }
        }
    }
    for (auto it = skinInfoMap.begin(); it != skinInfoMap.end(); ++it){
        DVKVertexSkin& info = it->second;
        for (int32_t i = info.used; i < 4; ++i){
            info.indices[i] = 0;
            info.weights[i] = 0.0f;
        }
    }
    mesh->isSkin = true;
}

glsl 顶点着色器:

#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec3  inPosition;
layout (location = 1) in vec2  inUV0;
layout (location = 2) in vec3  inNormal;
layout (location = 3) in vec4  inSkinIndex;
layout (location = 4) in vec4  inSkinWeight;
layout (binding = 0) uniform MVPBlock {
    mat4 modelMatrix;
    mat4 viewMatrix;
    mat4 projectionMatrix;
} uboMVP;
#define MAX_BONES 64
layout (binding = 1) uniform BonesTransformBlock{
    mat4 bones[MAX_BONES];
} bonesData;
layout (location = 0) out vec2 outUV;
layout (location = 1) out vec3 outNormal;
layout (location = 2) out vec4 outColor;
void main() {
    mat4 boneMatrix = bonesData.bones[int(inSkinIndex.x)] * inSkinWeight.x;
    boneMatrix += bonesData.bones[int(inSkinIndex.y)] * inSkinWeight.y;
    boneMatrix += bonesData.bones[int(inSkinIndex.z)] * inSkinWeight.z;
    boneMatrix += bonesData.bones[int(inSkinIndex.w)] * inSkinWeight.w;
    mat4 modeMatrix   = uboMVP.modelMatrix * boneMatrix;
    mat3 normalMatrix = transpose(inverse(mat3(modeMatrix)));
    vec3 normal = normalize(normalMatrix * inNormal.xyz);

    outUV       = inUV0;
    outNormal   = normal;
    outColor    = inSkinWeight;
    
    gl_Position = uboMVP.projectionMatrix * uboMVP.viewMatrix * modeMatrix * vec4(inPosition.xyz, 1.0);
}

这是全部代码: dvkmodel.h

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>

#include "vulkanFrame.h"

#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
struct DVKVertex {
    glm::vec3 pos;
    glm::vec2 uvs;
    glm::vec3 normals;
    glm::vec4 boneIDs;
    glm::vec4 weights;
    static VkVertexInputBindingDescription GetInputBinding(){
        VkVertexInputBindingDescription vertexInputBinding = {};
        vertexInputBinding.binding = 0;
        vertexInputBinding.stride = sizeof(DVKVertex);
        vertexInputBinding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
        return vertexInputBinding;
    }
    static std::vector<VkVertexInputAttributeDescription>GetInputAttributes(){
        VkFormat format[] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT };
        uint32_t offset[] = { offsetof(DVKVertex, pos), offsetof(DVKVertex, uvs), offsetof(DVKVertex, normals), offsetof(DVKVertex, boneIDs), offsetof(DVKVertex, weights) };
        std::vector<VkVertexInputAttributeDescription> vertexInputAttributs(sizeof(format) / sizeof(VkFormat));
        for (size_t i = 0; i < vertexInputAttributs.size(); ++i) {
            vertexInputAttributs[i].binding = 0;
            vertexInputAttributs[i].location = i;
            vertexInputAttributs[i].format = format[i];
            vertexInputAttributs[i].offset = offset[i];
        }
        return vertexInputAttributs;
    }
};
struct DVKBoundingBox {
    glm::vec3 min;
    glm::vec3 max;
    glm::vec3 corners[8];
    DVKBoundingBox(){

    }
    DVKBoundingBox{

    }
};

struct DVKPrimitive{
    BufferInfo indexBuffer;
    BufferInfo vertexBuffer;
    std::vector<DVKVertex>vertices;
    std::vector<uint32_t> indices;
    int32_t               vertexCount = 0;
    int32_t               triangleNum = 0;
    DVKPrimitive(){
    }
    ~DVKPrimitive(){
    }
    void DrawOnly(VkCommandBuffer cmdBuffer){
        if (vertexBuffer.size){
            vkCmdDraw(cmdBuffer, vertices.size(), 1, 0, 0);
        }
        else{
            vkCmdDrawIndexed(cmdBuffer, vertices.size(), 1, 0, 0, 0);
        }
    }
    void BindOnly(VkCommandBuffer cmdBuffer){
        VkDeviceSize offset = 0;
        if (vertexBuffer.size){
            vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &vertexBuffer.buffer, &offset);
        }
        if (indexBuffer.size){
            vkCmdBindIndexBuffer(cmdBuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
        }
    }
    void BindDrawCmd(VkCommandBuffer cmdBuffer){
        BindOnly(cmdBuffer);
        DrawOnly(cmdBuffer);
    }
};

struct DVKMaterialInfo{
    std::string     diffuse;
    std::string     normalmap;
    std::string     specular;
};
struct DVKBone{
    std::string     name;
    int32_t         index = -1;
    int32_t         parent = -1;
    glm::mat4       inverseBindPose;
    glm::mat4       finalTransform;
};
struct DVKVertexSkin{
    int32_t     used = 0;
    int32_t     indices[4];
    glm::vec4   weights;
};
template <class ValueType>
struct DVKAnimChannel{
    std::vector<float>     keys;
    std::vector<ValueType> values;

    void GetValue(float key, ValueType& outPrevValue, ValueType& outNextValue, float& outAlpha){
        outAlpha = 0.0f;
        if (keys.size() == 0){
            return;
        }
        if (key <= keys.front()){
            outPrevValue = values.front();
            outNextValue = values.front();
            outAlpha = 0.0f;
            return;
        }
        if (key >= keys.back()){
            outPrevValue = values.back();
            outNextValue = values.back();
            outAlpha = 0.0f;
            return;
        }
        int32_t frameIndex = 0;
        for (int32_t i = 0; i < keys.size() - 1; ++i){
            if (key <= keys[i + 1]){
                frameIndex = i;
                break;
            }
        }
        outPrevValue = values[frameIndex + 0];
        outNextValue = values[frameIndex + 1];

        float prevKey = keys[frameIndex + 0];
        float nextKey = keys[frameIndex + 1];
        outAlpha = (key - prevKey) / (nextKey - prevKey);
    }
};
struct DVKAnimationClip {
    std::string                 nodeName;
    float                       duration;
    DVKAnimChannel<glm::vec3>   positions;
    DVKAnimChannel<glm::vec3>   scales;
    DVKAnimChannel<glm::quat>   rotations;
};
struct DVKAnimation{
    std::string name;
    float       time = 0.0f;
    float       duration = 0.0f;
    float       speed = 1.0f;
    std::unordered_map<std::string, DVKAnimationClip> clips;
};

struct DVKMesh{
    typedef std::vector<DVKPrimitive*> DVKPrimitives;
    DVKPrimitives       primitives;
    DVKBoundingBox      bounding;
    DVKNode* linkNode;
    std::vector<int32_t>bones;
    bool                isSkin = false;
    DVKMaterialInfo     material;
    int32_t             vertexCount;
    int32_t             triangleCount;

    DVKMesh()
        : linkNode(nullptr)
        , vertexCount(0)
        , triangleCount(0){

    }
    void BindOnly(VkCommandBuffer cmdBuffer){
        for (int i = 0; i < primitives.size(); ++i){
            primitives[i]->BindOnly(cmdBuffer);
        }
    }
    void DrawOnly(VkCommandBuffer cmdBuffer){
        for (int i = 0; i < primitives.size(); ++i){
            primitives[i]->DrawOnly(cmdBuffer);
        }
    }
    void BindDrawCmd(VkCommandBuffer cmdBuffer){
        for (int i = 0; i < primitives.size(); ++i){
            primitives[i]->BindDrawCmd(cmdBuffer);
        }
    }
    ~DVKMesh(){
        for (int i = 0; i < primitives.size(); ++i){
            delete primitives[i];
        }
        primitives.clear();
        linkNode = nullptr;
    }
};
struct DVKNode{
    std::string                 name;
    std::vector<DVKMesh*>       meshes;
    DVKNode* parent;
    std::vector<DVKNode*>       children;
    glm::mat4                   localMatrix;
    glm::mat4                   globalMatrix;
    DVKNode()
        : name("None")
        , parent(nullptr){
    }
    const glm::mat4& GetLocalMatrix() {
        return localMatrix;
    }
    glm::mat4& GetGlobalMatrix() {
        globalMatrix = localMatrix;
        if (parent) {
            //globalMatrix.Append(parent->GetGlobalMatrix());
            globalMatrix = parent->GetGlobalMatrix() * globalMatrix;
        }
        return globalMatrix;
    }
    void CalcBounds(DVKBoundingBox& outBounds) {
        if (meshes.size() > 0) {
            const glm::mat4& matrix = GetGlobalMatrix();
            for (int32_t i = 0; i < meshes.size(); ++i) {
                glm::vec3 mmin = matrix * glm::vec4(meshes[i]->bounding.min, 1.0f);
                glm::vec3 mmax = matrix * glm::vec4(meshes[i]->bounding.max, 1.0f);
                outBounds.min = glm::min(outBounds.min, mmin);
                outBounds.min = glm::min(outBounds.min, mmax);
                outBounds.max = glm::max(outBounds.max, mmin);
                outBounds.max = glm::max(outBounds.max, mmax);
            }
        }
        for (int32_t i = 0; i < children.size(); ++i) {
            children[i]->CalcBounds(outBounds);
        }
    }

    DVKBoundingBox GetBounds() {
        DVKBoundingBox bounds;
        bounds.min = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
        bounds.max = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
        CalcBounds(bounds);
        bounds.UpdateCorners();
        return bounds;
    }
    ~DVKNode(){
        for (int32_t i = 0; i < meshes.size(); ++i){
            delete meshes[i];
        }
        meshes.clear();
        for (int32_t i = 0; i < children.size(); ++i){
            delete children[i];
        }
        children.clear();
    }
};
class DVKModel{
public:
    DVKModel()
        : device(nullptr)
        , rootNode(nullptr){

    }
    ~DVKModel(){
        delete rootNode;
        rootNode = nullptr;
        device = nullptr;
        meshes.clear();
        linearNodes.clear();
        for (int32_t i = 0; i < bones.size(); ++i){
            delete bones[i];
        }
        bones.clear();
    }
    void LoadFromFile(VkDevice device, const std::string& filename);
protected:
    DVKNode* LoadNode(const aiNode* node, const aiScene* scene);
    DVKMesh* LoadMesh(const aiMesh* mesh, const aiScene* scene);
    void LoadBones(const aiScene* aiScene);
    void LoadSkin(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene);
    void LoadVertexDatas(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, std::vector<DVKVertex>& vertices, glm::vec3& mmax, glm::vec3& mmin, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene);
    void LoadIndices(std::vector<uint32_t>& indices, const aiMesh* aiMesh, const aiScene* aiScene);

    void LoadPrimitives(std::vector<DVKVertex>& vertices, std::vector<uint32_t>& indices, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene);
    void LoadAnim(const aiScene* aiScene);

public:
    typedef std::unordered_map<std::string, DVKNode*> NodesMap;
    typedef std::unordered_map<std::string, DVKBone*> BonesMap;
    VkDevice   device;
    DVKNode* rootNode;
    std::vector<DVKNode*>           linearNodes;
    std::vector<DVKMesh*>           meshes;
    NodesMap                        nodesMap;
    std::vector<DVKBone*>           bones;
    BonesMap                        bonesMap;
    std::vector<DVKAnimation>       animations;
    int32_t                         animIndex = -1;

private:
    bool                            loadSkin = false;
};

dvkmodel.cpp

#include "DVKModel.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/cimport.h>
void FillMatrixWithAiMatrix(glm::mat4& matrix, const aiMatrix4x4& aiMatrix) {
    matrix[0][0] = aiMatrix.a1;
    matrix[0][1] = aiMatrix.a2;
    matrix[0][2] = aiMatrix.a3;
    matrix[0][3] = aiMatrix.a4;
    matrix[1][0] = aiMatrix.b1;
    matrix[1][1] = aiMatrix.b2;
    matrix[1][2] = aiMatrix.b3;
    matrix[1][3] = aiMatrix.b4;
    matrix[2][0] = aiMatrix.c1;
    matrix[2][1] = aiMatrix.c2;
    matrix[2][2] = aiMatrix.c3;
    matrix[2][3] = aiMatrix.c4;
    matrix[3][0] = aiMatrix.d1;
    matrix[3][1] = aiMatrix.d2;
    matrix[3][2] = aiMatrix.d3;
    matrix[3][3] = aiMatrix.d4;
    matrix = glm::transpose(matrix);
}
void DVKModel::LoadFromFile(VkDevice device, const std::string& filename){
    this->device = device;
    int assimpFlags = aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenUVCoords | aiProcess_GenSmoothNormals;
    loadSkin = true;
    Assimp::Importer importer;
    const aiScene* scene = importer.ReadFile(filename, assimpFlags);
    LoadBones(scene);
    LoadNode(scene->mRootNode, scene);
    LoadAnim(scene);
}
void DVKModel::LoadBones(const aiScene* aiScene){
    std::unordered_map<std::string, int32_t> boneIndexMap;
    for (int32_t i = 0; i < (int32_t)aiScene->mNumMeshes; ++i){
        aiMesh* aimesh = aiScene->mMeshes[i];
        for (int32_t j = 0; j < (int32_t)aimesh->mNumBones; ++j){
            aiBone* aibone = aimesh->mBones[j];
            std::string name = aibone->mName.C_Str();
            auto it = boneIndexMap.find(name);
            if (it == boneIndexMap.end()){
                // new bone
                int32_t index = (int32_t)bones.size();
                DVKBone* bone = new DVKBone();
                bone->index = index;
                bone->parent = -1;
                bone->name = name;
                FillMatrixWithAiMatrix(bone->inverseBindPose, aibone->mOffsetMatrix);
                // 记录Bone信息
                bones.push_back(bone);
                bonesMap.insert(std::make_pair(name, bone));
                // cache
                boneIndexMap.insert(std::make_pair(name, index));
            }
        }
    }
}
void DVKModel::LoadSkin(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene){
    std::unordered_map<int32_t, int32_t> boneIndexMap;
    for (int32_t i = 0; i < (int32_t)aiMesh->mNumBones; ++i){
        aiBone* boneInfo = aiMesh->mBones[i];
        std::string boneName(boneInfo->mName.C_Str());
        int32_t boneIndex = bonesMap[boneName]->index;
        // bone在mesh中的索引
        int32_t meshBoneIndex = 0;
        auto it = boneIndexMap.find(boneIndex);
        if (it == boneIndexMap.end()){
            meshBoneIndex = (int32_t)mesh->bones.size();
            mesh->bones.push_back(boneIndex);
            boneIndexMap.insert(std::make_pair(boneIndex, meshBoneIndex));
        }
        else{
            meshBoneIndex = it->second;
        }
        for (uint32_t j = 0; j < boneInfo->mNumWeights; ++j){
            uint32_t vertexID = boneInfo->mWeights[j].mVertexId;
            float  weight = boneInfo->mWeights[j].mWeight;
            if (skinInfoMap.find(vertexID) == skinInfoMap.end()){
                skinInfoMap.insert(std::make_pair(vertexID, DVKVertexSkin()));
            }
            DVKVertexSkin* info = &(skinInfoMap[vertexID]);
            info->indices[info->used] = meshBoneIndex;
            info->weights[info->used] = weight;
            ++info->used;
            if (info->used >= 4){
                break;
            }
        }
    }
    for (auto it = skinInfoMap.begin(); it != skinInfoMap.end(); ++it){
        DVKVertexSkin& info = it->second;
        for (int32_t i = info.used; i < 4; ++i){
            info.indices[i] = 0;
            info.weights[i] = 0.0f;
        }
    }

    mesh->isSkin = true;
}
void DVKModel::LoadVertexDatas(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, std::vector<DVKVertex>& vertices, glm::vec3& mmax, glm::vec3& mmin, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene) {
    for (int32_t i = 0; i < (int32_t)aiMesh->mNumVertices; ++i){
        DVKVertex v;
        v.pos = glm::vec3(aiMesh->mVertices[i].x, aiMesh->mVertices[i].y, aiMesh->mVertices[i].z);
        if (aiMesh->HasTextureCoords(0))
            v.uvs = glm::vec2(aiMesh->mTextureCoords[0][i].x, aiMesh->mTextureCoords[0][i].y);
        v.normals = glm::vec3(aiMesh->mNormals[i].x, aiMesh->mNormals[i].y, aiMesh->mNormals[i].z);
        DVKVertexSkin& skin = skinInfoMap[i];
        if (mesh->isSkin) {
            v.boneIDs = glm::vec4(skin.indices[0], skin.indices[1], skin.indices[2], skin.indices[3]);//默认0
            v.weights = glm::vec4(skin.weights[0], skin.weights[1], skin.weights[2], skin.weights[3]);//默认1, 0, 0, 0
        }
        else {
            v.boneIDs = glm::vec4(.0f);
            v.weights = glm::vec4(1.0f, .0f, .0f, .0f);
        }
        vertices.push_back(v);
    }
}
void DVKModel::LoadIndices(std::vector<uint32_t>& indices, const aiMesh* aiMesh, const aiScene* aiScene){
    for (int32_t i = 0; i < (int32_t)aiMesh->mNumFaces; ++i){
        indices.push_back(aiMesh->mFaces[i].mIndices[0]);
        indices.push_back(aiMesh->mFaces[i].mIndices[1]);
        indices.push_back(aiMesh->mFaces[i].mIndices[2]);
    }
}
void DVKModel::LoadPrimitives(std::vector<DVKVertex>& vertices, std::vector<uint32_t>& indices, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene){
    std::unordered_map<uint32_t, uint32_t> indicesMap;
    DVKPrimitive* primitive = new DVKPrimitive();
    primitive->vertices = vertices;
    primitive->indices = indices;
    createBuffer(device, indices.size() * sizeof(uint32_t), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitive->indexBuffer);
    bufferData(device, indices.size() * sizeof(uint32_t), indices.data(), primitive->indexBuffer.memory);
    createBuffer(device, vertices.size() * sizeof(DVKVertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitive->vertexBuffer);
    bufferData(device, vertices.size() * sizeof(DVKVertex), vertices.data(), primitive->vertexBuffer.memory);
    mesh->primitives.push_back(primitive);
}
DVKMesh* DVKModel::LoadMesh(const aiMesh* aiMesh, const aiScene* aiScene){
    DVKMesh* mesh = new DVKMesh();
    // load material
    aiMaterial* material = aiScene->mMaterials[aiMesh->mMaterialIndex];
    // load bones
    std::unordered_map<uint32_t, DVKVertexSkin> skinInfoMap;
    if (aiMesh->mNumBones > 0 && loadSkin){
        LoadSkin(skinInfoMap, mesh, aiMesh, aiScene);
    }
    // load vertex data
    std::vector<DVKVertex> vertices;
    glm::vec3 mmin(FLT_MAX, FLT_MAX, FLT_MAX);
    glm::vec3 mmax(-FLT_MAX, -FLT_MAX, -FLT_MAX);
    LoadVertexDatas(skinInfoMap, vertices, mmax, mmin, mesh, aiMesh, aiScene);
#else
    Vector3 mmin(MAX_FLT, MAX_FLT, MAX_FLT);
    Vector3 mmax(-MAX_FLT, -MAX_FLT, -MAX_FLT);
    LoadVertexDatas(skinInfoMap, vertices, mmax, mmin, mesh, aiMesh, aiScene);
    // load indices
    std::vector<uint32_t> indices;
    LoadIndices(indices, aiMesh, aiScene);
    // load primitives
    LoadPrimitives(vertices, indices, mesh, aiMesh, aiScene);
    mesh->bounding.min = mmin;
    mesh->bounding.max = mmax;
    mesh->bounding.UpdateCorners();
    return mesh;
}
DVKNode* DVKModel::LoadNode(const aiNode* aiNode, const aiScene* aiScene){
    DVKNode* vkNode = new DVKNode();
    vkNode->name = aiNode->mName.C_Str();
    if (rootNode == nullptr){
        rootNode = vkNode;
    }
    // mesh
    if (aiNode->mNumMeshes > 0){
        for (uint32_t i = 0; i < aiNode->mNumMeshes; ++i){
            DVKMesh* vkMesh = LoadMesh(aiScene->mMeshes[aiNode->mMeshes[i]], aiScene);
            vkMesh->linkNode = vkNode;
            vkNode->meshes.push_back(vkMesh);
            meshes.push_back(vkMesh);
        }
    }
    // nodes map
    nodesMap.insert(std::make_pair(vkNode->name, vkNode));
    linearNodes.push_back(vkNode);
    // bones parent
    int32_t boneParentIndex = -1;{
        auto it = bonesMap.find(vkNode->name);
        if (it != bonesMap.end()){
            boneParentIndex = it->second->index;
        }
    }
    // children node
    for (int32_t i = 0; i < (int32_t)aiNode->mNumChildren; ++i){
        DVKNode* childNode = LoadNode(aiNode->mChildren[i], aiScene);
        childNode->parent = vkNode;
        vkNode->children.push_back(childNode);

        // bones relationship
        {
            auto it = bonesMap.find(childNode->name);
            if (it != bonesMap.end()){
                it->second->parent = boneParentIndex;
            }
        }
    }
    return vkNode;
}
void DVKModel::LoadAnim(const aiScene* aiScene) {
    for (int32_t i = 0; i < (int32_t)aiScene->mNumAnimations; ++i) {
        aiAnimation* aianimation = aiScene->mAnimations[i];
        float timeTick = aianimation->mTicksPerSecond != 0 ? (float)aianimation->mTicksPerSecond : 25.0f
        animations.push_back(DVKAnimation());
        DVKAnimation& dvkAnimation = animations.back();

        for (int32_t j = 0; j < (int32_t)aianimation->mNumChannels; ++j){
            aiNodeAnim* nodeAnim = aianimation->mChannels[j];
            std::string nodeName = nodeAnim->mNodeName.C_Str();

            dvkAnimation.clips.insert(std::make_pair(nodeName, DVKAnimationClip()));

            DVKAnimationClip& animClip = dvkAnimation.clips[nodeName];
            animClip.nodeName = nodeName;
            animClip.duration = 0.0f;

            // position
            for (int32_t index = 0; index < (int32_t)nodeAnim->mNumPositionKeys; ++index){
                aiVectorKey& aikey = nodeAnim->mPositionKeys[index];
                animClip.positions.keys.push_back((float)aikey.mTime / timeTick);
                animClip.positions.values.push_back(glm::vec3(aikey.mValue.x, aikey.mValue.y, aikey.mValue.z));
                animClip.duration = glm::max((float)aikey.mTime / timeTick, animClip.duration);
            }

            // scale
            for (int32_t index = 0; index < (int32_t)nodeAnim->mNumScalingKeys; ++index){
                aiVectorKey& aikey = nodeAnim->mScalingKeys[index];
                animClip.scales.keys.push_back((float)aikey.mTime / timeTick);
                animClip.scales.values.push_back(glm::vec3(aikey.mValue.x, aikey.mValue.y, aikey.mValue.z));
                animClip.duration = glm::max((float)aikey.mTime / timeTick, animClip.duration);
            }

            // rotation
            for (int32_t index = 0; index < (int32_t)nodeAnim->mNumRotationKeys; ++index){
                aiQuatKey& aikey = nodeAnim->mRotationKeys[index];
                animClip.rotations.keys.push_back((float)aikey.mTime / timeTick);
                animClip.rotations.values.push_back(glm::quat(aikey.mValue.w, aikey.mValue.x, aikey.mValue.y, aikey.mValue.z));
                animClip.duration = glm::max((float)aikey.mTime / timeTick, animClip.duration);
            }
            dvkAnimation.duration = glm::max(animClip.duration, dvkAnimation.duration);
        }
    }
}
void AppendScale(glm::mat4& m, const glm::vec3& scale) {
    glm::mat4 matrix;
    matrix = glm::mat4(1.0f);
    matrix[0][0] = scale.x;
    matrix[1][1] = scale.y;
    matrix[2][2] = scale.z;
    m = matrix * m;
}
void DVKModel::GotoAnimation(float time) {
    if (animIndex == -1) {
        return;
    }
    DVKAnimation& animation = animations[animIndex];
    animation.time = glm::clamp(time, 0.0f, animation.duration);
    // update nodes animation
    for (auto it = animation.clips.begin(); it != animation.clips.end(); ++it){
        DVKAnimationClip& clip = it->second;
        DVKNode* node = nodesMap[clip.nodeName];

        float alpha = 0.0f;
        // rotation
        glm::quat prevRot(1, 0, 0, 0);
        glm::quat nextRot(1, 0, 0, 0);
        clip.rotations.GetValue(animation.time, prevRot, nextRot, alpha);
        glm::quat retRot = glm::lerp(prevRot, nextRot, alpha);

        // position
        glm::vec3 prevPos(0, 0, 0);
        glm::vec3 nextPos(0, 0, 0);
        clip.positions.GetValue(animation.time, prevPos, nextPos, alpha);
        glm::vec3 retPos = prevPos + alpha * (nextPos - prevPos);//MMath::Lerp(prevPos, nextPos, alpha);

        // scale
        glm::vec3 prevScale(1, 1, 1);
        glm::vec3 nextScale(1, 1, 1);
        clip.scales.GetValue(animation.time, prevScale, nextScale, alpha);
        glm::vec3 retScale = prevScale + alpha * (nextScale - prevScale);
        node->localMatrix = glm::mat4(1.0f);
        AppendScale(node->localMatrix, retScale);
        node->localMatrix *= glm::mat4_cast(retRot);
        node->localMatrix[3][0] += retPos.x;
        node->localMatrix[3][1] += retPos.y;
        node->localMatrix[3][2] += retPos.z;
    }
    // update bones
    for (int32_t i = 0; i < bones.size(); ++i){
        DVKBone* bone = bones[i];
        DVKNode* node = nodesMap[bone->name];
        // 注意行列矩阵的区别
        bone->finalTransform = bone->inverseBindPose;
        bone->finalTransform = node->GetGlobalMatrix() * bone->finalTransform;
    }
}

我没有详细查看您的代码,但略读了一下,您似乎没有使用 aiNode::mTransformation,您必须使用它才能获得正确的转换 w.r.t.骨骼的父骨骼。 ASSIMP 的文档对此参数的描述如下:

    /** The transformation relative to the node's parent. */
    C_STRUCT aiMatrix4x4 mTransformation;

并且在将 ASSIMP 的矩阵(行优先)转换为通常用于 OpenGL 或 Vulkan 的矩阵(通常是列优先)时必须小心。以下代码在这种情况下进行必要的转换:

/** Convert from a row-major ASSIMP matrix to a column-major GLM matrix */
glm::mat4 to_mat4(const aiMatrix4x4& aAssimpMat)
{
    return glm::transpose(*reinterpret_cast<const glm::mat4*>(&aAssimpMat[0][0]));
}