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)
- 执行顶点蒙皮
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;
因此:
最后我找到了解决方案:)所有的计算都是正确的,除了:
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;
}
}
}