所以我有以下 C++ 代码来计算发送到顶点着色器的最终骨骼矩阵:
void Skeleton::UpdateBoneTransforms(const Animation& animation, float animationTime, Bone& pNode, const glm::mat4& parentTransform)
{
auto global_transform = glm::mat4(1.0f);
bool has_animation = animation.m_BoneAnimations.find(pNode.index) != animation.m_BoneAnimations.end();
if (pNode.index != boneHierarchy.index && has_animation)
{
const auto& node_anim = animation.m_BoneAnimations.at(pNode.index);
auto translation = node_anim.GetInterpolatedPosition(animationTime);
auto translation_matrix = glm::translate(glm::mat4(1.0f), glm::vec3(translation.x, translation.y, translation.z));
auto rotation = node_anim.GetInterpolatedRotation(animationTime);
auto rotation_matrix = glm::toMat4(rotation);
auto scale = node_anim.GetInterpolatedScale(animationTime);
auto scale_matrix = glm::scale(glm::mat4(1.0f), glm::vec3(scale.x, scale.y, scale.z));
auto nodeTransform = translation_matrix * rotation_matrix * scale_matrix;
global_transform = parentTransform * nodeTransform;
boneTransformMatrices[pNode.index] = global_transform * boneOffsetMatrices[pNode.index];
}
for (auto& child : pNode.children)
UpdateBoneTransforms(animation, animationTime, child, global_transform);
}
现在这工作正常,但我发现一个模型,其中指尖骨骼有一个恒等的逆绑定矩阵,动画没有使指尖骨骼变形的通道,但它们各自的 gltf 节点确实具有非零平移和旋转。阅读 gltf 规范,我似乎无法弄清楚在这个矩阵数学中节点的本地属性应该去哪里?如果我无法插入任何关键帧,我是否应该在 0.0 秒处使用节点的本地属性作为单个关键帧?
是的,当没有 glTF 动画以该节点为目标播放时,该节点的本地平移/旋转/缩放修改器将生效。当动画播放时,其每个输出都针对特定节点的特定通道,在动画持续时间内覆盖节点的本地变换。
您提到逆绑定矩阵对于其中一些节点来说是恒等的,但节点的局部平移/旋转不是。在这种情况下,蒙皮将“永久”使网格变形,将其从原始网格数据中存储的绑定姿势更改为默认姿势。
这可能不是很常见,因为模型作者可能只是烘焙了默认姿势并完全摆脱了骨架。但万一他们认为骨架稍后可用于其他目的,是的,在这种状态下运送 glTF 是有效的。