因此,我正在使用2D骨骼动画系统。
X根骨头,每个骨头至少有1个部分(一个四边形,两个三角形)。平均而言,我可能有20块骨头和30个零件。大多数骨骼都依赖于父对象,骨骼会每帧移动一次。每个动画总共最多有1000帧,而我正在使用大约50个动画。一次最多可将大约50,000个帧加载到内存中。这些部分在骨架实例之间有所不同。
我采用的第一种方法是计算每个骨骼的位置/旋转,并为每个部分建立一个由该骨骼组成的顶点数组:
[x1,y1,u1,v1],[x2,y2,u2,v2],[x3,y3,u3,v3],[x4,y4,u4,v4]
并将其传递给每帧glDrawElements。
看起来不错,涵盖了我需要的所有方案,不占用太多内存,但是表现得很像狗。在iPod 4上,渲染其中的10个骨骼可能会达到15fps。
我得出的结论是,每帧复制大量顶点数据会消耗掉大部分性能。我决定走到另一个极端,并“预先计算”动画,在每个角色的开头建立一个顶点缓冲区,该缓冲区在单个角色中包含每个帧,每个部分的xyuv坐标。然后,我计算应在特定时间使用的帧的索引,并计算增量值,该值将传递到用于在当前帧和下一帧XY位置之间进行插值的着色器。
每帧的顶点看起来像这样
[--------------------- 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)来优化它。
还存在失去骨骼依赖性的问题。如果有两个骨骼,即父级和子级,并且子级的关键帧分别为0s和2s,则父级的关键帧分别为0s,0.5s,1.5s,2s,则子级将不会在0.5s和2s之间更改。应该是1.5s。
我提出了解决此骨骼问题的解决方案-通过强制 child 在与 parent 相同的位置设置关键帧。但这会使用更多的内存,并且基本上杀死了骨骼层次结构。
这就是我现在的位置。我试图在性能和内存使用之间找到平衡。我知道这里有很多冗余信息(特定部分的所有帧的UV坐标都相同,因此重复约30次)。并且必须为每组零件创建一个新的缓冲区(它们具有唯一的XYUV坐标-由于不同零件的尺寸不同,位置也会发生变化)
现在,我将尝试为每个字符设置一个顶点数组,其中所有部分都有xyuv,并计算每个部分的矩阵,然后将它们重新放置在着色器中。我知道这行得通,但是我担心性能不会比仅在开始时为每个帧上传XYUV更好。
有没有更好的方法可以做到这一点而又不损失我获得的性能呢?
有什么我可以尝试的荒谬想法吗?
最佳答案
更好的方法是动态转换30个零件,而不是在不同位置复制数千个零件。您的顶点缓冲区将包含您的顶点数据的一个副本,从而节省了大量内存。然后,可以使用对glDrawElements()
的调用,以统一方式将一组变换传递给顶点着色器,以代表绘制的每个骨骼,从而表示每个框架。每个相关骨骼的变换都是相对于父骨骼构建的。然后,根据您希望动画在手工制作和程序生成之间的连续性的位置而定,您的变换集可能会占用或多或少的空间和CPU计算时间。
Jason L. McKesson的免费书籍Learning Modern 3D Graphics Programming在第6章中很好地说明了如何实现此目的。本章末尾的示例程序显示了如何使用矩阵堆栈来实现分层模型。 I have an OpenGL ES 2.0 on iOS port of this program available。