努力在 DX12 中实现 assimp 骨架

问题描述 投票:0回答:0

最近我一直在尝试将 assimp 实现到 Frank Luna 的基本 dx12 引擎中,作为我学习的一部分。我在让矩阵数学正常工作时遇到了真正的麻烦,并且遇到了一些死胡同。

这是我用来从骨架和动画中获取转换并传递给 GPU 的主要函数。

首先,这里有一个 github 链接供任何喜欢直接查看源代码的人使用:repo

void Skeleton::GetTransforms(float timePos, aiNode* node, aiAnimation* animation, aiMatrix4x4& parentTransform, const aiMatrix4x4& globalInverseTransform, std::vector<DirectX::XMFLOAT4X4>& transforms)
{
    std::string nodeName(node->mName.data);
    aiMatrix4x4 nodeTransform = node->mTransformation;
    const aiNodeAnim* nodeAnim = FindNodeAnim(animation, nodeName);

    if (nodeAnim)
    {
        aiVector3D scaling;
        aiQuaternion rotation;
        aiVector3D translate;

        CalcInterpolatedScaling(scaling, timePos, nodeAnim);
        CalcInterpolatedRotation(rotation, timePos, nodeAnim);
        CalcInterpolatedPosition(translate, timePos, nodeAnim);

        nodeTransform = CreateAffineMatrix(scaling, rotation, translate);
    }

    aiMatrix4x4 globalTransform = parentTransform * nodeTransform;

    if (this->bones.find(nodeName) != this->bones.end())
    {
        int boneIndex = this->bones[nodeName].index;
        aiMatrix4x4 finalTransform = globalInverseTransform * globalTransform * this->bones[nodeName].offsetMatrix;
        aiMatConvert(finalTransform, transforms[boneIndex]);
    }

    for (UINT i = 0u; i < node->mNumChildren; i++)
    {
        GetTransforms(timePos, node->mChildren[i], animation, globalTransform, globalInverseTransform, transforms);
    }
}

此代码与我在其他几个线程中发现的代码非常吻合:

不幸的是,这没有产生正确的结果,目前这产生了一个奇怪的结果,如下所示:

Broken Result

如果我通过注释掉禁用动画

nodeTransform = CreateAffineMatrix(scaling, rotation, translate);

结果如我所料,人物坐在TPose:

Result without animation transforms

我一直无法弄清楚我的代码库是如何工作不正常的,这些是一些我已经尝试解决问题的方法

  • 转置结果
  • 转置 offsetMatrix
  • 使用 .glb 和 .gltf 而不是 fbx
  • SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false)
  • 使用一个简单的例子,即一个有 3 或 4 个关节的盒子

一个可能有用的观察,我注意到这个问题不会发生在面向世界空间的关节上。所以基本上他们没有一个独特的方向。这让我觉得

globalInverseTransform * globalTransform * this->bones[nodeName].offsetMatrix
可能无法正常工作。

我正在使用此功能采购 globalInverseTransform:

struct Animation
{
    Skeleton* skeleton;
    aiNode* rootNode;
    aiAnimation* animation;
    std::vector<DirectX::XMFLOAT4X4> transforms;
    float TimePos = 0.0f;
    bool Loop = true;
    float Speed = 1.0f;

    void UpdateSkinnedAnimation(float dt)
    {
        dt *= Speed;
        TimePos += dt;

        if (Loop)
        {
            float duration = animation->mDuration;
            if (TimePos > duration)
            {
                TimePos = 0.0;
            }
        }
        
        skeleton->GetTransforms(TimePos, rootNode, animation, aiMatrix4x4(), rootNode->mTransformation.Inverse(), transforms);

    }
};

rootNode 指针在网格加载阶段设置,数据存储为堆内存,因此在运行时不会丢失其范围。

其余的骨架相关功能如下所示,

非常感谢!这真的让我难过了一个星期,不确定下一步该看哪种方式,一些帮助将不胜感激! :)

float clamp(const float& f)
{
    return (f < 0.0f) ? 0.0f : ((f > 1.0f) ? 1.0f : f);
}

void aiMatConvert(const aiMatrix4x4& aiMatrix, DirectX::XMFLOAT4X4& dXMatrix)
{
    DirectX::XMMATRIX meshToBoneTransform = DirectX::XMMATRIX(aiMatrix.a1, aiMatrix.a2, 
                                            aiMatrix.a3, aiMatrix.a4, aiMatrix.b1, aiMatrix.b2, 
                                            aiMatrix.b3, aiMatrix.b4, aiMatrix.c1, aiMatrix.c2, 
                                            aiMatrix.c3, aiMatrix.c4, aiMatrix.d1, aiMatrix.d2, 
                                            aiMatrix.d3, aiMatrix.d4);

    DirectX::XMStoreFloat4x4(&dXMatrix, meshToBoneTransform);
}

const aiNodeAnim* Skeleton::FindNodeAnim(const aiAnimation* animation, const std::string& nodeName)
{
    for (unsigned int i = 0; i < animation->mNumChannels; ++i)
    {
        const aiNodeAnim* nodeAnim = animation->mChannels[i];
        if (std::string(nodeAnim->mNodeName.data) == nodeName)
        {
            return nodeAnim;
        }
    }

    return nullptr;
}

int Skeleton::FindPositionKey(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
    for (int i = 0; i < pNodeAnim->mNumPositionKeys - 1; i++) {
        if (AnimationTime < (float)pNodeAnim->mPositionKeys[i + 1].mTime) {
            return i;
        }
    }
    return 0;
}


int Skeleton::FindRotationKey(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
    for (int i = 0; i < pNodeAnim->mNumRotationKeys - 1; i++) {
        if (AnimationTime < (float)pNodeAnim->mRotationKeys[i + 1].mTime) {
            return i;
        }
    }
    return 0;
}

int Skeleton::FindScalingKey(float AnimationTime, const aiNodeAnim* pNodeAnim)
{
    for (int i = 0; i < pNodeAnim->mNumScalingKeys - 1; i++) {
        if (AnimationTime < (float)pNodeAnim->mScalingKeys[i + 1].mTime) {
            return i;
        }
    }
    return 0;
}

void Skeleton::CalcInterpolatedPosition(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
    if (pNodeAnim->mNumPositionKeys == 1) {
        Out = pNodeAnim->mPositionKeys[0].mValue;
        return;
    }

    int PositionIndex = FindPositionKey(AnimationTime, pNodeAnim);
    int NextPositionIndex = (PositionIndex + 1);
    
    float DeltaTime = (float)(pNodeAnim->mPositionKeys[NextPositionIndex].mTime - pNodeAnim->mPositionKeys[PositionIndex].mTime);
    float Factor = clamp((AnimationTime - (float)pNodeAnim->mPositionKeys[PositionIndex].mTime) / DeltaTime);
   
    const aiVector3D& Start = pNodeAnim->mPositionKeys[PositionIndex].mValue;
    const aiVector3D& End = pNodeAnim->mPositionKeys[NextPositionIndex].mValue;
    aiVector3D Delta = End - Start;
    
    Out = Start + Factor * Delta;
}

void Skeleton::CalcInterpolatedRotation(aiQuaternion& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
    if (pNodeAnim->mNumRotationKeys == 1) {
        Out = pNodeAnim->mRotationKeys[0].mValue;
        return;
    }

    int RotationIndex = FindRotationKey(AnimationTime, pNodeAnim);
    int NextRotationIndex = (RotationIndex + 1);
    
    float DeltaTime = (float)(pNodeAnim->mRotationKeys[NextRotationIndex].mTime - pNodeAnim->mRotationKeys[RotationIndex].mTime);
    float Factor = clamp((AnimationTime - (float)pNodeAnim->mRotationKeys[RotationIndex].mTime) / DeltaTime);
    
    const aiQuaternion& StartRotationQ = pNodeAnim->mRotationKeys[RotationIndex].mValue;
    const aiQuaternion& EndRotationQ = pNodeAnim->mRotationKeys[NextRotationIndex].mValue;
    aiQuaternion::Interpolate(Out, StartRotationQ, EndRotationQ, Factor);
    
    Out = Out.Normalize();
}

void Skeleton::CalcInterpolatedScaling(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
{
    if (pNodeAnim->mNumScalingKeys == 1) {
        Out = pNodeAnim->mScalingKeys[0].mValue;
        return;
    }

    int ScalingIndex = FindScalingKey(AnimationTime, pNodeAnim);
    int NextScalingIndex = (ScalingIndex + 1);
    
    float DeltaTime = (float)(pNodeAnim->mScalingKeys[NextScalingIndex].mTime - pNodeAnim->mScalingKeys[ScalingIndex].mTime);
    float Factor = clamp((AnimationTime - (float)pNodeAnim->mScalingKeys[ScalingIndex].mTime) / DeltaTime);
    
    const aiVector3D& Start = pNodeAnim->mScalingKeys[ScalingIndex].mValue;
    const aiVector3D& End = pNodeAnim->mScalingKeys[NextScalingIndex].mValue;
    aiVector3D Delta = End - Start;
    
    Out = Start + Factor * Delta;
}

aiMatrix4x4 CreateAffineMatrix(const aiVector3D& scaling, const aiQuaternion& rotation, const aiVector3D& translate)
{
    aiMatrix4x4 scalingMatrix;
    aiMatrix4x4 translationMatrix;
    aiMatrix4x4::Scaling(scaling, scalingMatrix);
    aiMatrix4x4::Translation(translate, translationMatrix);
    aiMatrix4x4 rotationMatrix = aiMatrix4x4(rotation.GetMatrix());

    return scalingMatrix * rotationMatrix * translationMatrix;
}

void Mesh::ReadSkeleton(const aiScene* scene, Skeleton* mSkeleton)
{
    unsigned int numMesh = scene->mNumMeshes;
    aiMesh** meshList = scene->mMeshes;

    int boneCount = 0;
    for (UINT x = 0; x < numMesh; ++x)
    {
        for (UINT i = 0; i < meshList[x]->mNumBones; ++i)
        {
            std::string boneName(meshList[x]->mBones[i]->mName.C_Str());
            bool exists = mSkeleton->bones.find(boneName) != mSkeleton->bones.end();

            if (!exists)
            {
                aiMatrix4x4& offsetMatrix = meshList[x]->mBones[i]->mOffsetMatrix;
                Joint joint(boneName, boneCount, offsetMatrix);
                mSkeleton->bones[boneName] = joint;
                boneCount += 1;
            }
        }
    }
}

void Mesh::ReadAnimations(const aiScene* scene, std::unordered_map<std::string, aiAnimation*> animations)
{
    unsigned int numAnim = scene->mNumAnimations;

    for (UINT x = 0; x < numAnim; ++x)
    {
        animations[scene->mAnimations[x]->mName.C_Str()] = scene->mAnimations[x];
    }
}
c++ directx assimp skinning directx-12
© www.soinside.com 2019 - 2024. All rights reserved.