Optimizing the Animation Pipeline
Optimizing the Animation Pipeline
Pre-generating the skin matrix
顶点着色器的最大的问题是系统会占用大量的Uniform
槽位。我们可以把pose matrix
和inverse bind pose
在CPU中计算好了再传入。这样我们就可以节约很多槽位(480个)。
我们可以统一先计算这两个矩阵相乘,然后修改CPU和GPU蒙皮的实现,使他们连个传入的参数都为这两个矩阵相乘。
Storing the skin pallette in a texture
我们可以使用FLOAT32
的贴图来储存动画矩阵,这种贴图每个顶点中的每个元素都可以拥有32位的浮点数。我们可以把动画矩阵转存到贴图中去,然后在顶点函数中进行采样即可。这种方式我们可以减少槽位道一个。但是他的缺点也很明显就是速度。使用采样的速度比直接使用数组查询会慢很多。
Faster sampling
因为我们在对每个Track
采样时,在给定时间后需要去定位这个时间是属于哪个Frame
中的,然后对其插值。这样的话我们就需要去遍历整个Frame
列表去找到合适两帧去插值。这个遍历就非常的消耗,因为我们需要对每个骨骼做这个操作。当这个动画切片时间非常长时,这个循环就会非常的久。
我们可以把所有Frame
做归一化。把整个列表映射到一个固定的时间间隔的查询表中。比如说我们假设每秒钟采样60次,那么我们的时间键可以1/60,然后新建一个以1/60时间为间隔的列表,把Frame
列表映射到这个列表中。
The Pose palette generation
在求得一个姿势的系列矩阵时,我们需要把每个节点的Transform
转换为矩阵,同时他是世界坐标的,所以我们就需要把本地的转化为世界空间中。
在从本地空间转化到世界空间时,我们需要对每个节点迭代他的父节点,直到父节点为根节点时。
这个计算是有一点浪费的,我们遍历可能会多次计算同一个父节点。
如果说计算节点从骨骼的根节点计算到尾节点,也就是从上到下依次每层来计算。这样在计算时,我们就不需要每次重新计算当前节点的父节点的世界空间,因为之前我们就计算过了。
我们可以使用缓存世界空间的方式来做,可以分两个循环来做,第一个循环找到和缓存世界矩阵。如果节点的父节点的索引小于节点的索引,说明我们之前计算过这个父节点的世界矩阵,反之我们就需要跳出第一个循环。
第二循环是使用原来的方式跌代求每个节点的世界矩阵。
这个方式的前提就是需要我们把越上层的父节放在数组的越前面。
Exploring Pose::GetGlobalTransform
因为我们随时都可能获取一个节点的世界坐标,所以我们可以设置两个数组来缓存世界坐标,一个存储世界坐标,一个存储标记当前节点是否需要更新世界坐标。
这种做法唯一的缺点就是会增加大量的内存。
这里的的优化只适用于有很多关键帧的动画。如果帧少的话我们可以使用二分查找。