优化OpenGL ES中骨骼动画的顶点



所以我正在使用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]

并将其传递到每个帧的gldrawelement。

看起来不错,涵盖了我需要的所有场景,不使用太多记忆,而是像狗一样。在iPod 4上,可能会得到15fps,其中10个骨架被渲染。

我弄清楚了大多数性能都通过复制每个帧的顶点数据而被食用。我决定转到另一个极端,并"预先计算"动画,在每个角色的开始时建立一个顶点缓冲区,其中每个帧中包含Xyuv坐标,每个部分,单个角色,单个字符。然后,我计算了应在特定时间内使用的框架的索引,并计算出Delta值,该值将通过用于插入电流和下一个框架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,0.5s,1.5s,2s中都有一个密钥帧,那么孩子不会在0.5s之间更改和1.5秒。

我提出了一种解决这个骨头问题的解决方案 - 强迫孩子在与父母相同的位置上拥有密钥框架。但这使用了更多的记忆,基本上杀死了骨骼层次结构的点。

这就是我现在的位置。我试图在性能和内存使用之间找到平衡。我知道这里有很多冗余信息(对于特定部分的所有帧,紫外线坐标都是相同的,因此重复约30次)。并且必须为每组零件(具有唯一的XYUV坐标 - 位置变化,因为不同的零件是不同的尺寸)

都必须创建一个新的缓冲区。

现在,我将尝试设置每个字符的一个顶点阵列,该字符具有所有零件的XYUV,并计算每个部分的矩阵,并将它们重新定位在着色器中。我知道这会起作用,但是我担心表演不会比我一开始要做的每个框架上传的XYUV更好。

有没有更好的方法可以做到这一点,而不会失去我获得的表现?

我可以尝试任何狂野的想法吗?

做到这一点的更好方法是即时改变您的30个零件,而不是将零件的数千份在不同位置。您的顶点缓冲区将包含您的顶点数据的一个副本,从而节省了大量的内存。然后,每个帧都可以用一组转换作为统一的转换来表示您绘制的每个骨头的均匀,并通过呼叫glDrawElements()来表示。每个依赖的骨骼的转化都是相对于父骨的。然后,取决于手工制作和程序生成的连续体的位置,您想要动画,您的转换集可能会占用或多或少的空间和CPU计算时间。

Jason L. McKesson的免费书,学习现代3D图形编程,就如何在第6章中完成此操作提供了一个很好的解释。本章结尾处的示例程序显示了如何使用矩阵堆栈以实现分层模型。我在此程序的iOS端口上有一个OpenGL ES 2.0。

最新更新