我只是看着我的动画精灵代码,并得到一些想法 通过改变tex coords制作动画。它有缓冲对象,它保存当前帧纹理坐标,当新帧请求时,新的纹理坐标通过 glBufferData()在缓冲区中提供。
如果我们预先计算所有动画帧纹理坐标,将它们放在BO中并使用一些帧创建索引缓冲区对象,我们需要绘制
GLbyte cur_frames = 0; //1,2,3 etc
现在我们需要更新动画,我们所需要的只是更新1个字节(而不是4 / 四个顶点数 / * 2 / s,t / *用于使用glBufferData的IBO的TRIANGLE_STRIP框架进行四边形绘制的sizeof(GLfloat)字节,我们不需要在BO初始化之后保持任何纹理坐标。
我错过了什么?什么是对比?
编辑:当然,您的顶点数据可能不是gl_float,例如。
答案 0 :(得分:1)
正如Tim所说,这取决于你的应用程序,让我们谈谈一些数字,你提到IBO的和将所有帧的纹理坐标插入到一个VBO中,所以让我们来看看每个人的影响。
假设一个典型的顶点如下所示:
struct vertex
{
float x,y,z; //position
float tx,ty; //Texture coordinates
}
我添加了一个z组件,但如果你不使用它,或者你有更多的属性,计算是相似的。所以很明显这个属性需要20个字节。
让我们假设一个简单的精灵:四边形,由2个三角形组成。在一个非常天真的模式中,您只需发送2x3顶点并将6*20=120
个字节发送到GPU。
在索引编制中,您知道实际上只有四个顶点:1,2,3,4
和两个三角形1,2,3
和2,3,4
。所以我们向GPU发送两个缓冲区:一个包含4个顶点(4*20=80
字节),另一个包含三角形的索引列表([1,2,3,2,3,4]
),假设我们可以在2字节(65535)中执行此操作索引应该足够了),所以这归结为6*2=12
字节。在总共92
个字节中,我们保存了28
个字节或大约23%
。此外,当渲染GPU可能only process each vertex once in the vertex shader时,它也为我们节省了一些处理能力。
所以,现在你想一次为所有动画添加所有纹理坐标。首先要注意的是,索引渲染中的顶点是由其所有属性定义的,您不能将其拆分为位置索引和纹理坐标索引。因此,如果要添加额外的纹理坐标,则必须重复这些位置。因此,您添加的每个“帧”将向VBO添加80
个字节,向IBO添加12
个字节。假设您有64个帧,最后得到64*(80+12)=5888
个字节。假设你有1000个精灵,那么这将变成大约6MB
。这似乎并不太糟糕,但请注意它的扩展速度非常快,每个帧都会增加大小,但也会增加每个属性(因为它们必须重复)。
那么,它有什么收获?
80
个字节或640
位。假设您需要以每秒1000
帧的速度每帧30
精灵执行此操作,您将获得19200000
个bps或19.2Mbps
(不包括任何开销)。这是非常低的(例如16xPCI-e可以处理32Gbps
),但如果你有其他带宽问题(例如由于纹理),它可能是值得的。此外,如果您仔细构建VBO(例如单独的VBO或非交错),您可以将其简化为仅更新纹理部分,在上面的示例中每个sprite只有16
个字节,这可能会减少带宽更。 最后,您还可以简单地将动画图像分割为很多纹理。我完全不知道它如何缩放,但在这种情况下你甚至不必使用更复杂的顶点属性,你只需为每一帧动画激活另一个纹理。
编辑:另一种方法可以是在制服中传递帧编号,并在采样之前在片段着色器中进行计算。设置一个整数均匀应该是一个很大的开销。
答案 1 :(得分:1)
对于现代GPU,访问/解包单个字节不一定比访问整数类型甚至向量(寄存器大小和加载指令等)更快。你可以节省内存,从而节省内存带宽,但我不希望这会对所有其他顶点属性数组访问产生很大的影响。
我认为,为动画精灵提供帧索引的最快方法是统一,或者如果必须使用一次绘制调用渲染多个精灵,则使用实例顶点属性数组。对于后者,您可以为固定大小的顶点子序列提供单个索引。 例如,在绘制'sprite-quads'时,每4个顶点有一个帧索引提取。 当使用实例化渲染时,第三种方法是缓冲区纹理。
我推荐用于时间/帧索引计算的全局(共享)统一,因此您可以在着色器中动态计算动画索引,这不需要您更新索引缓冲区(然后只表示相对精灵中的动画状态)