使用 Assimp.Net 和 OpenTK 换肤
Skinning with Assimp.Net and OpenTK
我正在尝试使用 Assimp.net 和 OpenTK 实现骨骼动画,并且一直在关注这个 tutorial 但我无法让它工作。
该模型在使用单位矩阵时看起来很好,但在使用我从 Assimp 生成的转换时出现严重的乱码。
我怀疑问题是我组合所有矩阵的方式,或者我没有意识到 OpenTK 中存在差异。我按照此处的建议对教程进行了类似的调整:
但它仍然是乱码,只是不同,我还尝试在执行任何乘法之前将所有 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 个月前修复了这个问题,但我的记忆很模糊...
如果我没记错的话,我的主要问题是 bone/vertex 索引,我想我搞砸了 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);
}
}
顶部的矩阵转换也是正确的,着色器代码也是如此。
我正在尝试使用 Assimp.net 和 OpenTK 实现骨骼动画,并且一直在关注这个 tutorial 但我无法让它工作。
该模型在使用单位矩阵时看起来很好,但在使用我从 Assimp 生成的转换时出现严重的乱码。
我怀疑问题是我组合所有矩阵的方式,或者我没有意识到 OpenTK 中存在差异。我按照此处的建议对教程进行了类似的调整:
但它仍然是乱码,只是不同,我还尝试在执行任何乘法之前将所有 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 个月前修复了这个问题,但我的记忆很模糊...
如果我没记错的话,我的主要问题是 bone/vertex 索引,我想我搞砸了 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);
}
}
顶部的矩阵转换也是正确的,着色器代码也是如此。