2012-01-28 72 views
0

我目前正在在iOS游戏里,长话短说,我需要借鉴了很多移动的立方体 - 200每帧最大近似。强调移动因为是的,我确实在这个主题上搜索了几个小时,并且还没有找到合适的解决方案来快速高效地绘制多个对象,其位置更新每一帧。绘制多个移动物体

通过对这个问题我无穷无尽的研究大多数人似乎提维也纳组织,但我不知道这会适合我的情况下每个对象的位置变化的每一帧。我现在使用OpenGL 1 - 我有工作代码和第3代/ 4 +设备(支持OpenGL 2的那些设备,哈)它以合理的帧率运行 - 然而,当测试我的(旧的,是的)第二代iPod touch,它非常缓慢,基本上无法播放。

我的代码包括顶点的静态数组的一个“立方体”和含每立方体的位置和颜色的阵列。我的游戏逻辑循环更新阵列中每个立方体的位置。此刻,我正在遍历多维数组数组,为每个多维数据集调用glTranslatef和glDrawArrays。从我读到的这个效率非常低,但是我对你如何优化它感到困惑。有任何想法吗?

(也许我不应该瞄准老,停产iOS设备,但给我的信念,我的代码是非常低效的,我想,这会帮助我今后的工作中,无论如果我找到一个方法来解决这个问题)

回答

0

对于这样的简单对象我会做一个大的VBO说200个对象* NrVerticesPerCube,把所有的数据交错的顶点,正常,UV,顶点,正常,UV等

我做的一个关键帧类似在我的游戏海狸的动画,我开始是这样的:

glGenBuffers(1, &vboObjects[vboGroupBeaver]); 
glBindBuffer(GL_ARRAY_BUFFER, vboObjects[vboGroupBeaver]); 
glBufferData(GL_ARRAY_BUFFER, beaverVerts*8*sizeof(GLfloat), 0, GL_STATIC_DRAW); 
vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES); 
NSString *path; 
path = [[NSBundle mainBundle] pathForResource:@"beaver01" ofType:@"bin"]; 
NSFileHandle *model = [NSFileHandle fileHandleForReadingAtPath:path]; 
float vertice[8]; 
int counter = 0; 
while (read([model fileDescriptor], &vertice, 8*sizeof(float))) { 
    memcpy(vbo_buffer, vertice, 8*sizeof(GLfloat));  // 0 
    vbo_buffer += 8*sizeof(GLfloat); 
    counter++; 
} 
glUnmapBufferOES(GL_ARRAY_BUFFER); 
glBindBuffer(GL_ARRAY_BUFFER, 0); 

这将创建一个具有正确大小我VBO缓冲区(在这种情况下,8 *的sizeof(GLfloat)至极是3绿党,3个法线和2UV ),并将第一个关键帧复制到缓冲区中,您可以对初始对象位置执行相同的操作,或者保留原来的对象位置并计算后者...

然后在每帧中我做2个关键帧之间插入我的海狸的每个顶点,只是做一个绘制调用,这是非常快的4029顶我的海狸,并在60FPS的作品在我的iPhone 3G。

对于你只是做gltranslates它会更简单,只是X,Y,Z值添加到每个立方体的每个顶点。

你会更新它是这样的:

glBindBuffer(GL_ARRAY_BUFFER, vboObjects[vboGroupBeaver]); 
    GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES); 

绑定VBO缓存和mapit地图到缓冲区变种。 计算您想要的临时变量的东西。

  memcpy(vbo_buffer, currentVert, 6*sizeof(GLfloat));  // 0 
      vbo_buffer += 8*sizeof(GLfloat); 

复制它和更新缓冲区来下一个对象,重复,直到所有对象更新中... 你也可以做的所有更新在一个单独的阵列和复制整个数组,但你会被复制的额外信息通常不会改变(法线和UV)。或者你可以不使用交错数据和复制...

 glUnmapBufferOES(GL_ARRAY_BUFFER); 

映射到该VBO缓冲

glVertexPointer(3, GL_FLOAT, 8*sizeof(GLfloat), (GLvoid*)((char*)NULL)); 
    glNormalPointer(GL_FLOAT, 8*sizeof(GLfloat), (GLvoid*)((char*)NULL+3*sizeof(GLfloat))); 
    glTexCoordPointer(2, GL_FLOAT,8*sizeof(GLfloat), (GLvoid*)((char*)NULL+6*sizeof(GLfloat))); 
    glDrawArrays(GL_TRIANGLES, 0, beaverVerts); 

设置你的绘图调用,并绘制了一切......

如果您需要要旋转的对象,而不仅仅是gltranslate他们,你将需要添加一些矩阵乘法沿途...

编辑**

好的,用手做gltranste其实很简单(旋转等等有点棘手)。

我使用的是使用TRIANGLE_STRIP而不是三角形绘制的交错平面,但原理是相同的。

float beltInter[] = { 
0.0, 0.0, 0.0,    // vertices[0] 
0.0, 0.0, 1.0,    // Normals [0] 
6.0, 1.0,     // UV [0] 
0.0, 480, 0.0,    // vertices[1] 
0.0, 0.0, 1.0,    // Normals [1] 
0.0, 1.0,     // UV  [1] 
320.0, 0.0, 0.0,   // vertices[2] 
0.0, 0.0, 1.0,    // Normals [2] 
6.0, 0.0,     // UV  [2] 
320.0, 480, 0.0,  // vertices[3] 
0.0, 0.0, 1.0,    // Normals [3] 
0.0, 0.0     // UV  [3] 
    }; 

所以这是交错的顶点,你有顶点,然后再法线紫外线,如果你不使用纹理代替UV颜色。

最简单的方法是将所有对象都放在一个数组中(如果所有对象都是相同的大小,则容易),并在绘制后更新位置(而不是在opengl框架的中间),更好的是做一个单独的线程,创建2个驻国际中心组织更新他们中的一个,而从其他拉丝,这样的事情:

  • 线程1的OpenGL DrawFrom VBO0
  • 线程2游戏更新,内部阵列上更新位置,并复制到VBO1 ,设置Var表示VBO1是准备好的(因此,当所有更新完成时,线程1仅从绘图变为VBO1)。
  • 线程1的OpenGL DrawFrom VBO1
  • 线程2游戏更新,同样的事情,但更新VBO0
  • 继续使用相同的逻辑

这就是所谓的双缓冲,你有时用它来garanty稳定,没有这个您的游戏逻辑会在显卡需要时更新VBO,并且显卡将不得不等待,从而导致更低的FPS。

无论如何,回到主题

使相当于的glTranslatef(10,20,30)只是做:

int maxvertices = 4; 
float x = 10; 
float y = 20; 
float z = 30; 
int counter = 0; 
int stride = 8;    // stride is 8 = 3 x vertice + 3 x normal + 2 x UV change to 3 x color or 4 x color depending on your needs 
glBindBuffer(GL_ARRAY_BUFFER, vboObjects[myObjects]); 
GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES); 
while (counter < (maxVertices*8)) { 
     beltInter[counter] += x; // just sum the corresponding values to each 
     beltInter[counter+1] += y; 
     beltInter[counter+2] += z; 
     memcpy(vbo_buffer, currentVert, 3*sizeof(GLfloat)); // again only copy what you need, in this case only copying the vertices, if your're updating all the data, you can just do a single memcpy at the end instead of these partial ones 
     vbo_buffer += stride*sizeof(GLfloat);  // forward the buffer 
     counter += stride;   // only update the vertex, but you could update everything 
} 
glUnmapBufferOES(GL_ARRAY_BUFFER); 

glVertexPointer(3, GL_FLOAT, stride*sizeof(GLfloat), (GLvoid*)((char*)NULL)); 
glNormalPointer(GL_FLOAT, stride*sizeof(GLfloat), (GLvoid*)((char*)NULL+3*sizeof(GLfloat))); 
glTexCoordPointer(2, GL_FLOAT,stride*sizeof(GLfloat), (GLvoid*)((char*)NULL+6*sizeof(GLfloat))); 
glDrawArrays(GL_TRIANGLE_STRIP, 0, maxVertices); 

当然的更新值不必是相同的所有的对象,事实上使用这样的基础数组,你可以随时更新所有信息,只需要将例程在需要时复制到VBO。所有这一切

从上飞记忆写的,所以有可能小龙:-)

希望有所帮助。

+0

感谢您的回答,但它仍然让我感到困惑。我将不得不计算并更新我的游戏对象循环中的VBO,对吗?我应该指出,我的'通用立方体'是一个由72个顶点组成的数组(类似于“1.0,0.0,0.0,...”) - 为了在一个数组中更新VBO,我需要计算基于立方体位置的每个立方体的每个顶点将会是glTranslate偏移量,对吧?我将如何能够存储颜色数据? – ttarik 2012-01-29 03:38:37

+0

是的,你会更新游戏循环中的VBO,每个立方体有72个顶点,所以你看14400个顶点?这对我的4029个顶点来说有点不利,但是我的主要游戏在3G上绘制了更多的顶点,它仍然可以工作。是的,但要做一个翻译,所有你需要做的就是将数值加到每个顶点,这非常快,我在我的插值中使用这种插值要差很多。颜色数据应该交错,我使用的纹理,你会coulou数据,而不是UV映射,并调用适当的glcolorpointer ... – led42 2012-01-29 12:02:19

+0

再次感谢。希望我的最后一个问题,我设置了这一点,并看着我的立方体顶点(http://puu.sh/f3Yz)和呃..我怎么会'总结值到每个顶点',以设置正确的位置?例如,如果我想翻译X +5和Z +10,那么每个顶点必须更改哪些内容? – ttarik 2012-01-30 03:01:46

0

您可以通过将所有多维数据集的所有坐标放在单个数组中,然后使用一个glDrawArrays调用进行绘制来进行优化。

我不知道为什么你要到立方体分成单独的阵列,也许除了因为它使你的数据结构更优雅/面向对象,但是这是我想看看做出的改善放在首位。

将立方体坐标转储到一个大数组中,并为每个立方体对象指定一个索引到该数组中,这样您仍然可以使更新逻辑保持相当分隔(如,立方体n拥有x到y范围内的坐标,负责更新它们,但是当您真正绘制坐标时,您直接在集中式坐标数组上运行glDrawArrays,而不是循环通过多维数据集对象并单独渲染它们。

+0

感谢您的回答。我对多个数组的意思是,其中一个是定义通用多维数据集顶点的静态数组,然后我的主数组包含多维数据集对象的数据。他们每个人都打圈,glTranslated到他们的位置,然后我的'通用立方体'被绘制。我怎么能够将所有的顶点组合成一个单一的数组? – ttarik 2012-01-28 16:12:33

+0

我也意识到了这种方法的潜在问题 - 我的立方体有不同的颜色。目前,我在绘制每个立方体的循环中使用glColor,如何将所有这些数据放在一个数组中? – ttarik 2012-01-29 03:28:38

+0

除了顶点数组之外,您还可以添加一个单独的颜色数组。这意味着指定每个顶点的颜色,而不是每个立方体的颜色,但我不认为会有明显的惩罚。 – 2012-01-29 09:53:06