骨骼蒙皮算法将模型脚下的所有东西都聚集在一起

问题描述

我正在尝试使用存储在 Collada 文件中的骨架动画来实现蒙皮,虽然我设法加载它并在没有正确蒙皮的情况下渲染模型,但我无法弄清楚为什么当我应用蒙皮算法时所有部分挤在模特的脚边,或极度变形。整个项目存储在 GitHub 上以供参考(皮肤分支)。

我相信顶点着色器是正确的,因为如果我将身份变换传递给骨骼,我会得到我的认姿势模型,它会根据 .dae 文件中的骨骼动画计算骨骼变换,但不知何故已损坏。这就是我的问题的样子,而不是模型在认姿势下的样子:

with skinning

without skinning

我相信我的问题出在应用递归骨骼变换时的某个地方:

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

相信骨骼到顶点的权重被正确收集并上传到着色器,并且最终骨骼阵列统一被正确上传(但可能没有正确计算)。我也不确定矩阵乘法的顺序以及上传到着色器时是否要转置任何内容,尽管我每次尝试都尝试了两种方法

解决方法

如果有人遇到类似的问题,我的问题是我的关键帧骨骼变换与其余变换链的计算方式相比被转置,所以当我将它们相乘时,一切都变得疯狂。所以,跟踪哪些矩阵是左手的,哪些是右手的!