学习目标
- 熟悉蒙皮动画的术语;
- 学习网格层级变换在数学理论,以及如何遍历基于树结构的网格层级;
- 理解顶点混合的想法以及数学理论;
- 学习如何从文件加载动画数据;
- 学习如何在D3D中实现角色动画。
1 框架的层级结构

1.1 数学公式
例如,有下面的结构:

每根子骨骼的坐标系都可以跟父骨骼关联,第一根骨骼与世界坐标系关联:

如果矩阵A0是第一根骨骼的世界变换矩阵,A1是第二根骨骼变换到第一根骨骼的矩阵,往后依次类推,那么第i根骨骼变换到世界坐标系的变换矩阵就是:

在我们上述的例子中,M2 = A2A1A0, M1 = A1A0 and M0 = A0,就是每根骨骼对于的世界坐标系变换矩阵:

2 蒙皮网格
2.1 定义

高光的那整条骨骼链叫做骨架(skeleton)。3D几何模型叫做皮肤(skin)。皮肤顶点与绑定空间相关联(整个皮肤相关联的局部坐标系)。每个骨骼影响一系列子皮肤的位置和形状。
2.2 重置骨骼到根空间的变换公式
和上述不同的地方是,把各个骨骼变换到世界坐标系的矩阵拆解开,先找到变换到根空间的矩阵,然后变换到世界坐标系;第二个不同点是从下往上,这样比从上往下更高效。第n根骨骼的变换如下:

这里p是骨骼i的父骨骼的编号,toRootp从p的局部坐标系映射到根局部坐标系。
2.3 抵消变换(Offset Transform)
有一个小问题是,被骨骼影响的顶点并不在骨骼坐标系统中,而是在绑定空间中。所以在应用公式对顶点进行变换之前,我们先要将顶点从绑定空间变换到影响它的骨骼的空间中,所以叫抵消变换(offset transformation)。

所以现在可以定义一个最终变换:

2.4 对骨架进行动画
我们定义了一个骨架动画的类SkinnedData.h/.cpp在Skinned Mesh Demo中。
我们首先对每个骨骼单独在局部坐标系移动,然后考虑其父节点的移动,然后变换到根空间。
我们定义一些列动画的动画片段(animation clip):
///<summary>
/// Examples of AnimationClips are "Walk", "Run", "Attack", "Defend".
/// An AnimationClip requires a BoneAnimation for every bone to form
/// the animation clip.
///</summary>
struct AnimationClip
{
// Smallest end time over all bones in this clip.
float GetClipStartTime()const;
// Largest end time over all bones in this clip.
float GetClipEndTime()const;
// Loops over each BoneAnimation in the clip and interpolates
// the animation.
void Interpolate(float t, std::vector<XMFLOAT4X4>& boneTransforms)const;
// Animation for each bone.
std::vector<BoneAnimation> BoneAnimations;
};
我们可以使用unordered_map保存这些片段:
std::unordered_map<std::string, AnimationClip> mAnimations;
AnimationClip& clip = mAnimations["attack"];
最终,每个骨骼需要抵消变换矩阵,并且需要一个数据结构表示骨架结构。所以我们骨骼动画最终数据结构如下:
class SkinnedData
{
public:
UINT BoneCount()const;
float GetClipStartTime(const std::string& clipName)const;
float GetClipEndTime(const std::string& clipName)const;
void Set(std::vector<int>& boneHierarchy,
std::vector<DirectX::XMFLOAT4X4>& boneOffsets,
std::unordered_map<std::string, AnimationClip>& animations);
// In a real project, you’d want to cache the result if there was a
// chance that you were calling this several times with the same
// clipName at the same timePos.
void GetFinalTransforms(const std::string& clipName, float timePos,
std::vector<DirectX::XMFLOAT4X4>& finalTransforms)const;
private:
// Gives parentIndex of ith bone.
std::vector<int> mBoneHierarchy;
std::vector<DirectX::XMFLOAT4X4> mBoneOffsets;
std::unordered_map<std::string, AnimationClip> mAnimations;
};
2.5 计算最终变换
我们使用一个整形数组模拟骨架层级,第i个元素值是第i个骨骼的父骨骼ID,并且对应第i个offset transform,并且对应骨骼动画中的第i个骨骼的动画:

最低0.47元/天 解锁文章
721

被折叠的 条评论
为什么被折叠?



