我正在尝试使用存储在 Collada 文件中的骨骼动画来实现蒙皮,虽然我设法加载它并在没有正确蒙皮的情况下渲染模型,但我无法弄清楚为什么当我应用我的蒙皮算法时,所有部分都会聚集在一起在模特的脚下,或极度变形。整个项目存储在GitHub 上以供参考(皮肤分支)。
我相信顶点着色器是正确的,因为如果我将身份变换传递给骨骼,我会得到我的默认姿势模型,它会根据.dae
文件中以某种方式损坏的骨骼动画来计算骨骼变换。这是我的问题的样子,而不是模型在默认姿势下的样子:
我相信我的问题出在应用递归骨骼变换的某个地方:
public void Update(double deltaSec)
{
if (CurrentAnimationName is null) return;
var anim = animations[CurrentAnimationName];
currentAnimationSec = (currentAnimationSec + deltaSec) % anim.Duration.TotalSeconds;
void calculateBoneTransforms(BoneNode boneNode, Matrix4x4 parentTransform)
{
var bone = anim.Bones.FirstOrDefault(b => b.Id == boneNode.Id);
var nodeTransform = bone?[TimeSpan.FromSeconds(currentAnimationSec)] ?? boneNode.Transform;
var globalTransform = parentTransform * nodeTransform;
if (boneNode.Id >= 0)
for (int meshIdx = 0; meshIdx < perMeshData.Length; ++meshIdx)
perMeshData[meshIdx].FinalBoneMatrices[boneNode.Id] = globalTransform * perMeshData[meshIdx].boneOffsetMatrices[boneNode.Id];
foreach (var child in boneNode.Children)
calculateBoneTransforms(child, globalTransform);
}
calculateBoneTransforms(rootBoneNode, Matrix4x4.Identity);
}
或者在使用它们的变换构建骨骼数据的递归结构时:
BoneNode visitTransforms(Node node, Matrix4x4 mat)
{
var boneNode = new BoneNode
{
Children = new BoneNode[node.ChildCount],
Id = boneIds.TryGetValue(node.Name, out var id) ? id : -1,
Transform = Matrix4x4.Transpose(node.Transform.ToNumerics()),
};
mat = node.Transform.ToNumerics() * mat;
foreach (var meshIndex in node.MeshIndices)
transformsDictionary[scene.Meshes[meshIndex]] = mat;
int childIdx = 0;
foreach (var child in node.Children)
boneNode.Children[childIdx++] = visitTransforms(child, mat);
return boneNode;
}
rootBoneNode = visitTransforms(scene.RootNode, Matrix4x4.Identity);
我相信骨骼到顶点的权重被正确地收集并上传到着色器,并且最终的骨骼阵列制服被正确地上传(但可能没有正确计算)。我也不确定矩阵乘法的顺序以及在上传到着色器时是否要转置任何东西,尽管我每次尝试都尝试过两种方式。