我正在使用一个2D骨骼动画系统。
有X个骨头,每个骨头至少有一个部分(一个四边形,两个三角形)。平均而言,我可能有20个骨头和30个部分。大多数骨头依赖于父级,每帧骨头都会移动。每个动画总共多达1000帧,我使用了大约50个动画。任何时候都有约50000帧的内存负载。不同骨架实例之间的部件不同。
我采取的第一种方法是计算每个骨头的位置/旋转,并构建一个顶点数组,其中包括每个部分的以下内容:
[x1,y1,u1,v1],[x2,y2,u2,v2],[x3,y3,u3,v3],[x4,y4,u4,v4]
每帧都将其传递给glDrawElements。
这看起来很好,覆盖了我需要的所有场景,不占用太多内存,但是性能非常差。在iPod 4上,渲染10个这样的骨架可能只有15fps。
我发现大部分性能都被每帧复制的大量顶点数据所消耗。于是我决定采取另一个极端,“预先计算”动画,在每个角色开始时建立一个包含每个部位每一帧的xyuv坐标的顶点缓冲区。然后,我计算应该在特定时间使用哪个帧的索引,并计算一个增量值,通过传递到用于在当前和下一帧之间进行插值的着色器中。
每个帧的顶点如下所示:
[--------------------- Frame 1 ---------------------],[------- Frame 2 ------]
[x1,y1,u1,v1,boneIndex],[x2, ...],[x3, ...],[x4, ...],[x1, ...][x2, ...][....]
顶点着色器如下所示:
attribute vec4 a_position;
attribute vec4 a_nextPosition;
attribute vec2 a_texCoords;
attribute float a_boneIndex;
uniform mat4 u_projectionViewMatrix;
uniform float u_boneAlpha[255];
varying vec2 v_texCoords;
void main() {
float alpha = u_boneAlpha[int(a_boneIndex)];
vec4 position = mix(a_position, a_nextPosition, alpha);
gl_Position = u_projectionViewMatrix * position;
v_texCoords = a_texCoords;
}
现在,性能很好,屏幕上有10个图形,帧率保持在50fps左右。但现在它使用了大量的内存。我通过缩减xyuv的精度(现在是ushorts)来优化它。
还有一个问题是骨骼依赖关系丢失了。如果有两个骨头,一个父骨和一个子骨,子骨在0秒和2秒有一个关键帧,父骨在0秒、0.5秒、1.5秒、2秒有关键帧,则子骨在0.5秒到1.5秒之间不会像应该的那样发生变化。
我想出了一个解决这个骨骼问题的办法——强制让子骨在与父骨相同的时间点拥有关键帧。但这将使用更多的内存,基本上破坏了骨骼层次结构的重点。
这就是我现在所处的状态。我正在尝试在性能和内存使用之间寻找平衡。我知道这里有很多冗余信息(对于特定部分的所有帧,UV坐标都是相同的,因此重复了约30次)。而且每组零件都必须创建一个新的缓冲区(因为不同的零件大小不同,所以位置会改变)。
现在我要尝试为每个字符设置一个顶点数组,其中包含所有零件的xyuv,并计算每个零件的矩阵,并在着色器中重新定位它们。我知道这会起作用,但我担心性能不会比我一开始上传XYUV的每个帧好。
有没有更好的方法来做到这一点,而不失去我所获得的性能?
还有什么疯狂的想法可以尝试吗?