Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十三章:角色动画



学习目标

  1. 熟悉蒙皮动画的术语;
  2. 学习网格层级变换在数学理论,以及如何遍历基于树结构的网格层级;
  3. 理解顶点混合的想法以及数学理论;
  4. 学习如何从文件加载动画数据;
  5. 学习如何在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个骨骼的动画:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值