You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DirectX12结合Assimp实现骨骼动画时模型扭曲变形问题求助

DirectX12骨骼动画躯干手臂扭曲问题排查求助

我在基于DirectX12的C++游戏引擎中实现骨骼动画,使用Assimp库解析模型,运行时角色躯干和手臂出现扭曲变形。参照OpenGL教程实现,期望模型正常播放目标动画,但实际呈现扭曲状态(见附图)。已确认顶点与骨骼数据正确解析并上传,但应用动画时出现异常。

当使用m_LocalMatrix替代动画插值后的AnimatedTransform时,模型可正常渲染T姿势,但此时计算FinalTransformation需乘Offset矩阵。

动画实现代码

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

Shader代码(参考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至最新版本
  • 确保顶点数据类型正确
  • 保证C++中矩阵为行主序、HLSL中为列主序
  • 测试不同模型与动画,均出现相同扭曲问题
  • 重新从Blender导出模型

该问题已困扰多周,恳请提供技术帮助!

内容的提问来源于stack exchange,提问作者GCourtney7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 01:25:02