在DX12引擎中集成Assimp骨骼动画遇矩阵计算问题求助
Assimp集成DX12引擎骨骼动画矩阵计算问题
作为学习的一部分,我正尝试将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); } }
这段代码与多个技术线程中的实现高度一致。
遗憾的是,代码未产生正确结果,当前显示异常:
若注释掉nodeTransform = CreateAffineMatrix(scaling, rotation, translate);禁用动画,角色会正常显示为T姿:
我尚未定位到问题根源,已尝试以下解决方法:
- 转置最终结果矩阵
- 转置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]; } }
这个问题已经困扰我一周了,不确定下一步该排查哪里,非常感谢您的帮助!
内容的提问来源于stack exchange,提问作者Gregm8
相关产品推荐
相关产品推荐

