opengl - 使用 Assimp.Net 和 OpenTK 进行换肤

标签 opengl opentk skinning assimp

我正在尝试使用 Assimp.net 和 OpenTK 实现骨骼动画,并且一直在关注此 tutorial但我无法让它工作。

该模型在单位矩阵方面表现良好,但在使用我从 Assimp 生成的变换时却出现严重的乱码。

我怀疑问题在于我组合所有矩阵的方式,或者 OpenTK 中存在我没有意识到的差异。我根据教程进行了类似的调整,如下所示:Matrix calculations for gpu skinning

但它仍然是乱码,只是不同的是,我还尝试在执行任何乘法之前将所有 Assimp 矩阵转换为 OpenTK 矩阵。这些是与矩阵相关的代码区域,如果需要,我可以提供更多:

矩阵转换

public static OpenTK.Matrix4 TKMatrix(Assimp.Matrix4x4 input)
     {
        return new OpenTK.Matrix4(input.A1, input.B1, input.C1, input.D1,
                                   input.A2, input.B2, input.C2, input.D2,
                                   input.A3, input.B3, input.C3, input.D3,
                                   input.A4, input.B4, input.C4, input.D4);
     }

存储全局逆

public class LoaderMesh
  {
     public Scene mScene;
     public Mesh mMesh;

     public OpenTK.Matrix4 GlobalInverseTransform { get; set; }

     public LoaderMesh(Scene aiScene, Mesh aiMesh)
     {
        mScene = aiScene;
        mMesh = aiMesh;

        GlobalInverseTransform = Util.TKMatrix(mScene.RootNode.Transform);
        GlobalInverseTransform.Invert();
     }

加载骨骼

public void LoadBones(List<VBO.Vtx_BoneWeight.Vtx> boneData)
     {
        for (uint iBone = 0; iBone < mMesh.BoneCount; ++iBone)
        {
           uint boneIndex = 0;
           String bonename = mMesh.Bones[iBone].Name;

           if (!BoneMapping.ContainsKey(bonename))
           {
              boneIndex = (uint)NumBones;
              NumBones++;
              BoneInfo bi = new BoneInfo();
              BoneInfos.Add(bi);
           }
           else
           {
              boneIndex = BoneMapping[bonename];
           }

           BoneMapping[bonename] = boneIndex;
           BoneInfos[(int)boneIndex].OffsetMatrix = Util.TKMatrix(mMesh.Bones[iBone].OffsetMatrix);

           for (uint iWeight = 0; iWeight < mMesh.Bones[iBone].VertexWeightCount; iWeight++)
           {
              uint VertexID = /*m_Entries[MeshIndex].BaseVertex*/ mMesh.Bones[iBone].VertexWeights[iWeight].VertexID;
              float Weight = mMesh.Bones[iBone].VertexWeights[iWeight].Weight;

              VBO.Vtx_BoneWeight.Vtx vtx = boneData[(int)VertexID];

              VBO.Vtx_BoneWeight.AddWeight(ref vtx, boneIndex, Weight);

              boneData[(int)VertexID] = vtx;
           }
        }
     }

计算变换

public void ReadNodeHierarchy(float animationTime, Node aiNode, ref OpenTK.Matrix4 parentTransform)
     {
        String NodeName = aiNode.Name;

        Animation animation = mScene.Animations[0];

        OpenTK.Matrix4 NodeTransformation = Util.TKMatrix(aiNode.Transform);

        NodeAnimationChannel nodeAnim = FindNodeAnim(animation, NodeName);

        OpenTK.Matrix4 localTransform = OpenTK.Matrix4.Identity;

        if (nodeAnim != null)
        {
           // Interpolate scaling and generate scaling transformation matrix
           Vector3D Scaling = new Vector3D();
           CalcInterpolatedScaling(ref Scaling, animationTime, nodeAnim);
           Console.WriteLine("Scaling: " + Scaling.ToString());
           OpenTK.Matrix4 ScalingM = Util.TKMatrix(Matrix4x4.FromScaling(Scaling));

           // Interpolate rotation and generate rotation transformation matrix
           Quaternion RotationQ = new Quaternion();
           CalcInterpolatedRotation(ref RotationQ, animationTime, nodeAnim);
           Console.WriteLine("Rotation: " + RotationQ.ToString());
           OpenTK.Matrix4 RotationM = Util.TKMatrix(RotationQ.GetMatrix());

           // Interpolate translation and generate translation transformation matrix
           Vector3D Translation = new Vector3D();
           CalcInterpolatedPosition(ref Translation, animationTime, nodeAnim);
           Console.WriteLine("Transform: " + Translation.ToString());
           OpenTK.Matrix4 TranslationM = Util.TKMatrix(Matrix4x4.FromTranslation(Translation));

           // Combine the above transformations
           NodeTransformation = TranslationM * RotationM * ScalingM;

           localTransform = TranslationM * RotationM * ScalingM;
        }

        OpenTK.Matrix4 GlobalTransformation = parentTransform * NodeTransformation;

        OpenTK.Matrix4 parentPass = OpenTK.Matrix4.Identity;
        if (BoneMapping.ContainsKey(NodeName) == true)
        {
           uint BoneIndex = BoneMapping[NodeName];
           //BoneInfos[(int)BoneIndex].FinalTransformation = GlobalInverseTransform * BoneInfos[(int)BoneIndex].OffsetMatrix * GlobalTransformation;
           BoneInfos[(int)BoneIndex].NodeTransformation = parentTransform * Util.TKMatrix(aiNode.Transform) * localTransform;
           parentPass = BoneInfos[(int)BoneIndex].NodeTransformation;

           BoneInfos[(int)BoneIndex].FinalTransformation = GlobalInverseTransform * BoneInfos[(int)BoneIndex].NodeTransformation * BoneInfos[(int)BoneIndex].OffsetMatrix;
        }

        for (uint i = 0; i < aiNode.ChildCount; i++)
        {
           ReadNodeHierarchy(animationTime, aiNode.Children[i], ref parentPass);
        }
     }

这是顶点着色器代码

#version 400

layout(location = 0)in vec4 vert;
layout(location = 1)in vec4 normal;
layout(location = 2)in vec4 texCoord;
layout(location = 3)in vec4 tanCoord;

layout(location = 4)in ivec4 boneIDs;
layout(location = 5)in vec4 boneWeights;

uniform mat4 projectionMtx;
uniform mat4 viewMtx;
uniform mat4 modelMtx;

const int MAX_BONES = 100;

uniform mat4 bones[MAX_BONES];

out vec3 positionFrg_CS;
out vec3 normalFrg_CS;
out vec3 tanCoordFrg_CS;
out vec3 bitCoordFrg_CS;
out vec4 texCoordFrg;

void main()
{
   mat4 BoneTransform = bones[boneIDs[0]] * boneWeights[0];
    BoneTransform += bones[boneIDs[1]] * boneWeights[1];
    BoneTransform += bones[boneIDs[2]] * boneWeights[2];
    BoneTransform += bones[boneIDs[3]] * boneWeights[3];

    gl_Position = projectionMtx * viewMtx * modelMtx * BoneTransform * vert;


}

我在将矩阵相乘时做错了什么吗?

最佳答案

回复 livin_amuk,我已经得到了这个工作,至少足以满足我的需要,但是我在 6 个月前修复了这个问题,我的内存很模糊......

如果我没记错的话,我的主要问题是骨骼/顶点索引,我想我搞乱了 BaseVertex 因为我很匆忙。这是我当前工作的 LoadBones 函数。

public void LoadBones(List<VBO.Vtx_BoneWeight.Vtx> boneData, SubMesh mesh)
  {
     for (int iBone = 0; iBone < mesh.mMesh.BoneCount; ++iBone)
     {
        uint boneIndex = 0;
        String bonename = mesh.mMesh.Bones[iBone].Name;

        if (!BoneMapping.ContainsKey(bonename))
        {
           boneIndex = (uint)NumBones;
           NumBones++;
           BoneInfo bi = new BoneInfo();
           BoneInfos.Add(bi);

//Note, I have these two lines included inside the if statement, the original tut does not. Not sure if it makes a difference.
           BoneMapping[bonename] = boneIndex;
           BoneInfos[(int)boneIndex].OffsetMatrix = AssimpToOpenTK.TKMatrix(mesh.mMesh.Bones[iBone].OffsetMatrix);
        }
        else
        {
           boneIndex = BoneMapping[bonename];
        }

        for (int iWeight = 0; iWeight < mesh.mMesh.Bones[iBone].VertexWeightCount; iWeight++)
        {
//My question has the mesh.BaseVertex commented out. it is important!
           long VertexID = mesh.BaseVertex + mesh.mMesh.Bones[iBone].VertexWeights[iWeight].VertexID;
           float Weight = mesh.mMesh.Bones[iBone].VertexWeights[iWeight].Weight;

           VBO.Vtx_BoneWeight.Vtx vtx = boneData[(int)VertexID];

           VBO.Vtx_BoneWeight.AddWeight(ref vtx, boneIndex, Weight);

           boneData[(int)VertexID] = vtx;
        }
     }
  }

我还进行了向后转换。读取节点层次结构函数。

public void ReadNodeHierarchy(float animationTime, Node aiNode, ref OpenTK.Matrix4 parentTransform)
  {
     String NodeName = aiNode.Name;

     Animation animation = mScene.Animations[0];

     OpenTK.Matrix4 NodeTransformation = AssimpToOpenTK.TKMatrix(aiNode.Transform);

     NodeAnimationChannel nodeAnim = FindNodeAnim(animation, NodeName);

     if (nodeAnim != null)
     {
        // Interpolate scaling and generate scaling transformation matrix
        Vector3D Scaling = new Vector3D();
        CalcInterpolatedScaling(ref Scaling, animationTime, nodeAnim);
        OpenTK.Matrix4 ScalingM = AssimpToOpenTK.TKMatrix(Matrix4x4.FromScaling(Scaling));

        // Interpolate rotation and generate rotation transformation matrix
        Quaternion RotationQ = new Quaternion();
        CalcInterpolatedRotation(ref RotationQ, animationTime, nodeAnim);
        OpenTK.Matrix4 RotationM = AssimpToOpenTK.TKMatrix(RotationQ.GetMatrix());

        // Interpolate translation and generate translation transformation matrix
        Vector3D Translation = new Vector3D();
        CalcInterpolatedPosition(ref Translation, animationTime, nodeAnim);
        OpenTK.Matrix4 TranslationM = AssimpToOpenTK.TKMatrix(Matrix4x4.FromTranslation(Translation));

        // Combine the above transformations
        //All that local transform stuff is gone. The order of the transforms is reversed from my question AND the original tut.
        NodeTransformation = ScalingM * RotationM * TranslationM;
     }
//Also reversed.
     OpenTK.Matrix4 GlobalTransformation = NodeTransformation * parentTransform;

     //GlobalTransformation = OpenTK.Matrix4.Identity;
     if (BoneMapping.ContainsKey(NodeName) == true)
     {
        uint BoneIndex = BoneMapping[NodeName];
//Also, Also, reversed.
        BoneInfos[(int)BoneIndex].FinalTransformation = BoneInfos[(int)BoneIndex].OffsetMatrix * GlobalTransformation * GlobalInverseTransform;
     }

     for (int i = 0; i < aiNode.ChildCount; i++)
     {
        ReadNodeHierarchy(animationTime, aiNode.Children[i], ref GlobalTransformation);
     }
  }

顶部的矩阵转换也是正确的,着色器代码也是如此。

关于opengl - 使用 Assimp.Net 和 OpenTK 进行换肤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38805182/

相关文章:

c++ - 传递参数时创建的对象应该在哪里删除?

c# - 如何使用 OpenTK 绘制三角形?

cocoa - 使用 Webkit 在 OSX 上创建可换肤的自定义窗口

apache-flex - 如何在 Flex Spark Datagrid 中设置选择边框颜色

css - flex CSS 属性?

c - 像 "GLenum GlewInitResult = glewInit();"这样的声明(?)是做什么的?

c++ - OpenGL 子窗口不会同时响应

opengl - HOpenGL关于Haskell中的其他线程和TChans的行为如何?

c# - OpenTK C# Perspective Lighting 看起来有问题

c# - 垃圾收集不适用于 OpenTK,或者我是否缺少足够的处理?