我正在用自己的Collada解析器编写动画系统,并且遇到了一个无法解决的问题。
我收集了网格/皮肤信息(顶点,法线,jointIds,权重等),我的骨骼信息(关节,它们的局部变换,反向绑定位置,层次结构) 和我的动画(每个关节的关键帧变换位置,时间戳)。
我的问题是计算并随后在着色器中实现了所有内容(权重的总和乘以联合变换和顶点位置)-我得到以下信息:
当我删除权重乘法时,网格保持完整无缺-但是皮肤实际上并没有跟随动画。我感到好像数学是正确的一样迷茫,但是很显然我在某个地方出错了。有人可以照亮我误解的方面吗?
这是我目前的理解和实施方式:
// Recursively collect each Joints InverseBindTransform -
// root joint's local position is an identity matrix.
// Function is only called once after data collection.
void Joint::CalcInverseBindTransform(glm::mat4 parentLocalPosition)
{
glm::mat4 bindTransform = parentLocalPosition * m_LocalTransform;
m_InverseBindPoseMatrix = glm::inverse(bindTransform);
for (Joint child : Children) {
child.CalcInverseBindTransform(bindTransform);
}
}
std::unordered_map<int, glm::mat4> Animator::InterpolatePoses(float time) {
std::unordered_map<int, glm::mat4> poses;
if (IsPlaying()) {
for (std::pair<int, JointTransform> keyframe : m_PreviousFrame.GetJointKeyFrames()) {
JointTransform previousFrame = m_PreviousFrame.GetJointKeyFrames()[keyframe.first];
JointTransform nextFrame = m_NextFrame.GetJointKeyFrames()[keyframe.first];
JointTransform interpolated = JointTransform::Interpolate(previousFrame, nextFrame, time);
poses[keyframe.first] = interpolated.getLocalTransform();
}
}
return poses;
}
void Animator::ApplyPosesToJoints(std::unordered_map<int, glm::mat4> newPose, Joint* j, glm::mat4 parentTransform)
{
if (IsPlaying()) {
glm::mat4 currentPose = newPose[j->GetJointId()];
glm::mat4 modelSpaceJoint = parentTransform * currentPose;
for (Joint child : j->GetChildren()) {
ApplyPosesToJoints(newPose, &child, modelSpaceJoint);
}
modelSpaceJoint = glm::transpose(j->GetInvBindPosition() * modelSpaceJoint);
j->SetAnimationTransform(modelSpaceJoint);
}
}
void AnimationModel::Render(bool& pass)
{
[...]
std::vector<glm::mat4> transforms = GetJointTransforms();
for (int i = 0; i < transforms.size(); ++i) {
m_Shader->SetMat4f(transforms[i], ("JointTransforms[" + std::to_string(i) + "]").c_str());
}
[...]
}
void AnimationModel::AddJointsToArray(Joint current, std::vector<glm::mat4>& matrix)
{
glm::mat4 finalMatrix = current.GetAnimatedTransform();
matrix.push_back(finalMatrix);
for (Joint child : current.GetChildren()) {
AddJointsToArray(child, matrix);
}
}
for (int i = 0; i < total_weight_amnt; ++i) {
mat4 jointTransform = JointTransforms[jointIds[i]];
vec4 newVertexPos = jointTransform * vec4(pos, 1.0);
total_pos += newVertexPos * weights[i];
[...]
----------回复标准化权重------------
在1以上有一些权重之和,但是在解决了我的代码中的错误之后,该模型如下所示:
用于计算权重-我遍历向量中所有预加的权重,如果发现的权重小于要添加的权重,则在该位置替换该权重。否则,我将权重附加到向量的末尾。如果向量中的权重小于指定的max_weights(即4),则我将剩余的权重/关节ID填充为0。
答案 0 :(得分:0)
我了解蒙皮动画出现问题时,可能在不同区域出现 alot 问题。因此,对于将来遇到相同问题的Google员工-可以将其更多地看作是您可能做错了而不是绝对做错了。 / p>
对于我的问题-在许多小领域,我有正确的主意,但方法错误。距离我很近,但正如他们所说,没有雪茄。
我不需要自己计算逆绑定姿势,Collada的逆绑定姿势(有时/通常被声明为“ offsetMatrix”)非常完美。这不是问题,因为我只是在进行不必要的计算。
在Collada文件中,它们通常为您提供的层次结构中的“关节”或“节点”比动画所需的更多。在开始实际的动画“关节”之前,有场景和初始骨架“节点”类型。场景通常是一个身份矩阵,在读取Collada文件时会根据您的“上轴”对其进行操作。 Node类型将确定骨骼中每个关节的整体大小-因此,如果不调整大小,则可能是单位矩阵。确保您的层次结构仍然包含层次结构中列出的 ALL 个节点/关节。我非常不这样做,这极大地扭曲了我的globalPosition(BindPose)。
如果要通过四元数表示“关节”的变换旋转(强烈建议使用),请确保在两个旋转位置之间进行插值后将生成的四元数归一化。 同样,将“旋转”和“变换”合并到最终矩阵中时,请确保乘法顺序和最终输出正确。
最后-您的最后一个外观矩阵由关节InvBindMatrix * GlobalPosition * GlobalInverseRootTransform组成(<-这是(1)中提到的“场景”节点的局部变换的逆函数,还记得吗?)。 根据到目前为止的先前矩阵乘法,您可能需要转置最终矩阵。
并以此-我能够成功为模型制作动画!
最后一个注意事项-我的网格物体和动画文件是分别添加的。如果动画与网格物体位于不同的文件中,请确保从具有动画的文件而不是具有网格物体的文件中收集蒙皮/关节信息。我列出了加载模型的步骤,然后通过不同的文件为它提供了多个动画: