C# Assimp:手动操纵装配网格的骨骼

C# Assimp:手动操纵装配网格的骨骼,c#,3d,assimp,C#,3d,Assimp,我正在从事一个有以下目标的项目: 使用Assimp.NET加载装配的三维网格(例如人体骨骼) 操纵网格骨骼,使其适合您自己的身体(使用Microsoft Kinect v2) 执行顶点蒙皮 加载装配的网格和提取骨骼信息(希望)不会出现任何问题(基于本教程:)。每个骨骼(类别“ModelBone”)由以下信息组成: Assimp.Matrix4x4 LocalTransform Assimp.Matrix4x4 GlobalTransform Assimp.Matrix4x4 Offset

我正在从事一个有以下目标的项目:

  • 使用Assimp.NET加载装配的三维网格(例如人体骨骼)
  • 操纵网格骨骼,使其适合您自己的身体(使用Microsoft Kinect v2)
  • 执行顶点蒙皮
加载装配的网格和提取骨骼信息(希望)不会出现任何问题(基于本教程:)。每个骨骼(类别“ModelBone”)由以下信息组成:

Assimp.Matrix4x4 LocalTransform
Assimp.Matrix4x4 GlobalTransform
Assimp.Matrix4x4 Offset
LocalTransform
直接从assimp节点(
node.Transform
)提取

GlobalTransform
包括自己的
LocalTransform
和所有父级的
LocalTransform
(请参阅代码片段
calculateGlobalTransformation()

Offset
直接从assimp-bone(
bone.OffsetMatrix
)中提取

目前我还没有实现GPU顶点蒙皮,但我迭代每个顶点并操纵它的位置和法向量

        foreach (Vertex vertex in this.Vertices)
        {
            Vector3D newPosition = new Vector3D();
            Vector3D newNormal = new Vector3D();

            for (int i=0; i < vertex.boneIndices.Length; i++)
            {
                int boneIndex = vertex.boneIndices[i];
                float boneWeight = vertex.boneWeights[i];

                ModelBone bone = this.BoneHierarchy.Bones[boneIndex];

                Matrix4x4 finalTransform = bone.GlobalTransform * bone.Offset;

                // Calculate new vertex position and normal
                newPosition += boneWeight * (finalTransform * vertex.originalPosition);
                newNormal += boneWeight * (finalTransform * vertex.originalNormal);
            }

            // Apply new vertex position and normal
            vertex.position = newPosition;
            vertex.normal = newNormal;
        }
在骨骼操作后,我使用以下代码计算骨骼及其子骨骼的新
全局转换

    public void UpdateTransformations(ModelBone bone)
    {
        this.calculateGlobalTransformation(bone);

        foreach (var child in bone.Children)
        {
            UpdateTransformations(child);
        }
    }

    private void calculateGlobalTransformation(ModelBone bone)
    {
        // Global transformation includes own local transformation ...
        bone.GlobalTransform = bone.LocalTransform;

        ModelBone parent = bone.Parent;

        while (parent != null)
        {
            // ... and all local transformations of the parent bones (recursively)
            bone.GlobalTransform = parent.LocalTransform * bone.GlobalTransform;
            parent = parent.Parent;
        }
    }
这种方法导致了这种情况。转换似乎正确地应用于所有子骨骼,但操纵的骨骼围绕世界空间原点旋转,而不是围绕其自身的局部空间旋转:(我已尝试包含
GlobalTransform
转换(GlobalTransform的最后一行)将其设置为
LocalTransform
,但未成功

我希望有人能帮我解决这个问题


提前感谢!

要变换骨骼,应使用骨骼的偏移矩阵: 偏移矩阵从全局空间转换到骨骼空间。如果要围绕骨骼的原点旋转骨骼,应:

  • 变换到骨骼空间
  • 应用旋转
  • 变换到全局空间
  • 所以骨骼的全局变换可以这样计算:

    bonesGlobalTransform = parentGlobalTransform *
            bone.offset.inverse() * 
            boneLocalTransform * 
            bone.offset;
    
    因此:

  • 用偏移矩阵变换到骨骼空间
  • 应用局部变换
  • 使用offset.inverse()矩阵变换到全局空间

  • 最后我找到了解决方案:)所有的计算都是正确的,除了:

    Matrix4x4 finalTransform = bone.GlobalTransform * bone.Offset;
    
    对我来说,正确的计算方法是:

    Matrix4x4 finalTransform = bone.GlobalTransform * bone.Offset;
    finalTransform.transpose();
    
    因此,这似乎是一个主要的行/列问题。我的最后一个CPU顶点蒙皮代码是:

        public void PerformSmoothVertexSkinning()
        {
            // Precompute final transformation matrix for each bone
            List<Matrix4x4> FinalTransforms = new List<Matrix4x4>();
    
            foreach (ModelBone bone in this.BoneHierarchy.Bones)
            {
                // Multiplying a vector (e.g. vertex position/normal) by finalTransform will (from right to left):
                //      1. transform the vector from mesh space to bone space (by bone.Offset)
                //      2. transform the vector from bone space to world space (by bone.GlobalTransform)
                Matrix4x4 finalTransform = bone.GlobalTransform * bone.Offset;
                finalTransform.Transpose();
    
                FinalTransforms.Add(finalTransform);
            }
    
            foreach (Submesh submesh in this.Submeshes)
            {
                foreach (Vertex vertex in submesh.Vertices)
                {
                    Vector3D newPosition = new Vector3D();
                    Vector3D newNormal = new Vector3D();
    
                    for (int i = 0; i < vertex.BoneIndices.Length; i++)
                    {
                        int boneIndex = vertex.BoneIndices[i];
                        float boneWeight = vertex.BoneWeights[i];
    
                        // Get final transformation matrix to transform each vertex position
                        Matrix4x4 finalVertexTransform = FinalTransforms[boneIndex];
    
                        // Get final transformation matrix to transform each vertex normal (has to be inverted and transposed!)
                        Matrix4x4 finalNormalTransform = FinalTransforms[boneIndex];
                        finalNormalTransform.Inverse();
                        finalNormalTransform.Transpose();
    
                        // Calculate new vertex position and normal (average of influencing bones)
                        // Formula: newPosition += boneWeight * (finalVertexTransform * vertex.OriginalPosition);
                        //                      += boneWeight * (bone.GlobalTransform * bone.Offset * vertex.OriginalPosition);
                        // From right to left:
                        //      1. Transform vertex position from mesh space to bone space (by bone.Offset)
                        //      2. Transform vertex position from bone space to world space (by bone.GlobalTransform)
                        //      3. Apply bone weight
                        newPosition += boneWeight * (finalVertexTransform * vertex.OriginalPosition);
                        newNormal += boneWeight * (finalNormalTransform * vertex.OriginalNormal);
                    }
    
                    // Apply new vertex position and normal
                    vertex.Position = newPosition;
                    vertex.Normal = newNormal;
                }
            }
        }
    
    public void performMoothVertexSkinning()
    {
    //预计算每个骨骼的最终变换矩阵
    List FinalTransforms=新列表();
    foreach(此中的ModelBone.BoneHierarchy.Bones)
    {
    //将向量(例如顶点位置/法线)乘以最终变换(从右到左):
    //1.将向量从网格空间变换到骨骼空间(按bone.Offset)
    //2.将向量从骨骼空间变换到世界空间(通过bone.GlobalTransform)
    Matrix4x4 finalTransform=bone.GlobalTransform*bone.Offset;
    最终转换。转置();
    添加(最终转换);
    }
    foreach(在此子页面中的子页面。子页面)
    {
    foreach(子网格中的顶点顶点顶点顶点)
    {
    Vector3D newPosition=新Vector3D();
    Vector3D newNormal=新Vector3D();
    对于(int i=0;i

    希望这个帖子对其他人有帮助。谢谢你的帮助,谢尔盖

    嗨。我认为calculateGlobalTransformation和顶点蒙皮都是不正确的。你应该在calculateGlobalTransformation中使用偏移矩阵,而不应该在顶点蒙皮中使用偏移矩阵。Hey Sergey,我很高兴你回答了。几乎放弃了这个话题。自从上一篇文章以来,我改变了两件小事(矩阵乘法顺序)。因此,我已经更新了我的初始帖子(请参见
    calculateGlobalTransformation
    和顶点蒙皮代码),现在它反映了我当前的代码。我确信
    GlobalTransform
    的计算是正确的,因为在我的视口中将每个骨骼的
    GlobalTransform
    的平移部分显示为点时,它会生成以下图像:。因此,我认为问题在于蒙皮(例如错误的
    FinalTransform
        public void PerformSmoothVertexSkinning()
        {
            // Precompute final transformation matrix for each bone
            List<Matrix4x4> FinalTransforms = new List<Matrix4x4>();
    
            foreach (ModelBone bone in this.BoneHierarchy.Bones)
            {
                // Multiplying a vector (e.g. vertex position/normal) by finalTransform will (from right to left):
                //      1. transform the vector from mesh space to bone space (by bone.Offset)
                //      2. transform the vector from bone space to world space (by bone.GlobalTransform)
                Matrix4x4 finalTransform = bone.GlobalTransform * bone.Offset;
                finalTransform.Transpose();
    
                FinalTransforms.Add(finalTransform);
            }
    
            foreach (Submesh submesh in this.Submeshes)
            {
                foreach (Vertex vertex in submesh.Vertices)
                {
                    Vector3D newPosition = new Vector3D();
                    Vector3D newNormal = new Vector3D();
    
                    for (int i = 0; i < vertex.BoneIndices.Length; i++)
                    {
                        int boneIndex = vertex.BoneIndices[i];
                        float boneWeight = vertex.BoneWeights[i];
    
                        // Get final transformation matrix to transform each vertex position
                        Matrix4x4 finalVertexTransform = FinalTransforms[boneIndex];
    
                        // Get final transformation matrix to transform each vertex normal (has to be inverted and transposed!)
                        Matrix4x4 finalNormalTransform = FinalTransforms[boneIndex];
                        finalNormalTransform.Inverse();
                        finalNormalTransform.Transpose();
    
                        // Calculate new vertex position and normal (average of influencing bones)
                        // Formula: newPosition += boneWeight * (finalVertexTransform * vertex.OriginalPosition);
                        //                      += boneWeight * (bone.GlobalTransform * bone.Offset * vertex.OriginalPosition);
                        // From right to left:
                        //      1. Transform vertex position from mesh space to bone space (by bone.Offset)
                        //      2. Transform vertex position from bone space to world space (by bone.GlobalTransform)
                        //      3. Apply bone weight
                        newPosition += boneWeight * (finalVertexTransform * vertex.OriginalPosition);
                        newNormal += boneWeight * (finalNormalTransform * vertex.OriginalNormal);
                    }
    
                    // Apply new vertex position and normal
                    vertex.Position = newPosition;
                    vertex.Normal = newNormal;
                }
            }
        }