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

时间:2016-01-13 14:44:37

标签: c# 3d assimp

我正在开展一个有以下目标的项目:

  • 使用Assimp.NET
  • 加载装配好的3D网格(例如人体骨骼)
  • 操纵网格骨骼,使其适合您自己的身体(使用Microsoft Kinect v2)
  • 执行顶点蒙皮

加载绑定网格并提取骨骼信息(希望如此)没有任何问题(基于本教程:http://www.richardssoftware.net/2013/10/skinned-models-in-directx-11-with.html)。每个骨骼(类" 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;
        }

就像我已经说过的,我想用Kinect v2传感器操纵骨骼,所以我不必使用动画(例如插值关键帧,......)!但是一开始我希望能够手动操纵骨骼(例如,将网状物的躯干旋转90度)。因此,我通过调用Assimp.Matrix4x4.FromRotationX(1.5708f);创建一个4x4旋转矩阵(围绕x轴旋转90度)。然后我用这个旋转矩阵替换骨骼LocalTransform

Assimp.Matrix4x4 rotation = Assimp.Matrix4x4.FromRotationX(1.5708f);
bone.LocalTransform = rotation;

UpdateTransformations(bone);

在骨骼操作之后,我使用以下代码来计算骨骼及其子骨骼的新GlobalTransform

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

这种方法导致image。转换似乎正确地应用于所有子骨骼,但是被操纵的骨骼围绕世界空间原点而不是围绕其自己的局部空间旋转:(我已经尝试包括GlobalTransform翻译({{1的最后一行)在将其设置为GlobalTransform之前进入旋转矩阵,但没有成功......

我希望有人可以帮我解决这个问题!

提前致谢!

2 个答案:

答案 0 :(得分:0)

要变换骨骼,您应该使用它的偏移矩阵: http://assimp.sourceforge.net/lib_html/structai_bone.html#a9ae5293b5c937436e4b338e20221cc2e 偏移矩阵从全局空间转换到骨骼空间。如果你想围绕它的原点旋转骨骼,你应该:

  1. 转换为骨骼空间
  2. 申请轮换
  3. 转换为全球空间
  4. 因此骨骼的全局变换可以这样计算:

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

    所以:

    1. 使用偏移矩阵转换为骨骼空间
    2. 应用本地转换
    3. 使用offset.inverse()矩阵
    4. 转换为全局空间

答案 1 :(得分:0)

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

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

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