我正在尝试在我的游戏引擎中实现骨骼动画,但在使用 C++ 制作骨骼动画时遇到了问题。角色有动画,但躯干和手臂看起来扭曲。我正在关注 this OpenGL 教程,并期望模型看起来像 this,但它看起来像下面的视频。顶点和骨骼正在正确解析和上传,但是当我将动画应用于骨骼时,就会发生这种情况。
我已将所有骨骼组装到一个数组中,并保存它们的父级以完成骨骼层次结构,如this教程中所示。我相信问题出在此处,因为当我在第一个 for 循环中乘以 m_LocalMatrix 而不是 AnimatedTransform 时,模型以 T 姿势呈现 find 。但是,当我这样做时,在最后一个 for 循环中计算 FinalTransformation 矩阵时,我还必须乘以该骨骼的偏移矩阵。这是我的模型动画代码:
std::vector<FMatrix> LocalTransforms( m_SkeletalMesh->Joints.size() );
std::vector<FMatrix> ModelTransforms( m_SkeletalMesh->Joints.size() );
for (uint32 i = 0; i < m_SkeletalMesh->Joints.size(); i++)
{
FJoint& joint = m_SkeletalMesh->Joints[i];
if (m_anim->m_KeyFrames.find( joint.m_Name ) != m_anim->m_KeyFrames.end())
{
uint32 KeyIndex = 0;
uint32 NextKeyIndex = 0;
for (uint32 i = 0; i < m_anim->m_KeyFrames[joint.m_Name].size() - 1; i++)
{
if (AnimationTimeTicks < m_anim->m_KeyFrames[joint.m_Name][i + 1].m_Timestamp)
{
KeyIndex = i;
break;
}
}
NextKeyIndex = KeyIndex + 1;
float t1 = m_anim->m_KeyFrames[joint.m_Name][KeyIndex].m_Timestamp;
float t2 = m_anim->m_KeyFrames[joint.m_Name][NextKeyIndex].m_Timestamp;
float DeltaTime = t2 - t1;
float Factor = (AnimationTimeTicks - (float)t1) / DeltaTime;
HE_ASSERT( Factor >= 0.f && Factor <= 1.f );
FTransform& Start = m_anim->m_KeyFrames[joint.m_Name][KeyIndex].m_AnimatedTransform;
FTransform& End = m_anim->m_KeyFrames[joint.m_Name][NextKeyIndex].m_AnimatedTransform;
FTransform AnimatedTransform = FTransform::Interpolate( Start, End, Factor );
LocalTransforms[i] = AnimatedTransform.GetLocalMatrix();
}
else
{
LocalTransforms[i] = joint.m_LocalMatrix;
}
}
ModelTransforms[0] = LocalTransforms[0];
for (uint32 i = 1; i < m_SkeletalMesh->Joints.size(); i++)
{
FJoint& joint = m_SkeletalMesh->Joints[i];
ModelTransforms[i] = ModelTransforms[joint.m_ParentIndex] * LocalTransforms[i];
}
JointCBData* pJointCB = m_SkeletalMesh->m_JointCB.GetBufferPointer();
for (uint32 i = 0; i < m_SkeletalMesh->Joints.size(); i++)
{
FJoint& joint = m_SkeletalMesh->Joints[i];
FMatrix& FinalTransform = pJointCB->kJoints[i];
FinalTransform = m_SkeletalMesh->m_GlobalInverseTransform * ModelTransforms[i];
}
我确信层次结构已被正确解析,但这是我执行此操作的代码:
void ProcessJoints( std::vector<FJoint>& Joints, const aiNode* pNode, const uint32& ParentIndex, const aiMesh* pMesh )
{
FJoint& joint = Joints.emplace_back();
strcpy_s( joint.m_Name, sizeof( joint.m_Name ), pNode->mName.C_Str());
joint.m_NameHash = StringHash( joint.m_Name );
joint.m_ParentIndex = ParentIndex;
memcpy( &joint.m_LocalMatrix, &pNode->mTransformation, sizeof( FMatrix ) );
for (uint32 i = 0; i < pMesh->mNumBones; i++)
{
if (pNode->mName == pMesh->mBones[i]->mName)
{
memcpy( &joint.m_OffsetMatrix, &pMesh->mBones[i]->mOffsetMatrix, sizeof( FMatrix ) );
break;
}
}
uint32 NewParentIndex = (uint32)Joints.size() - 1u;
for (uint32 i = 0; i < pNode->mNumChildren; i++)
{
ProcessJoints( Joints, pNode->mChildren[i], NewParentIndex, pMesh );
}
}
这也是我的着色器代码,取自 Frank Luna 的 DirectX12 书籍:
float weights[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
weights[0] = Input.Weights[0];
weights[1] = Input.Weights[1];
weights[2] = Input.Weights[2];
weights[3] = 1.0f - weights[0] - weights[1] - weights[2];
float3 totalLocalPos = float3(0, 0, 0);
for (int i = 0; i < HE_MAX_JOINTS_PER_VERTEX; i++)
{
totalLocalPos += weights[i] * mul( float4(Input.Position, 0), Joints[Input.JointIDs[i]] ).xyz;
}
Result.Position = mul( float4(totalLocalPos, 1), WorldViewProjection );
我几周来一直在努力解决这个问题,但就是找不到问题所在。任何帮助将不胜感激!
我尝试过但没有成功的其他事情是:
Assimp 主要面向 OpenGL,因此要使其与 DirectX 兼容,您必须进行一些调整。
在
Assimp::Importer::ReadFile()
中,传递 Assimp 后处理标志 - aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder
。将属性 bool AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS
设置为 true
并检查 - 使用函数 Assimp::Importer::SetPropertyBool()
。
您也可以通过此链接。