我正在开发一款iOS游戏,长话短说,我需要绘制很多移动的立方体 - 每帧大约最多200个。强调移动,因为是的,我确实在这个主题上搜索了几个小时,并且还没有找到一个合适的解决方案来快速,有效地绘制多个对象,每个帧的位置都会更新。
通过我对这个主题的无数研究,大多数人似乎都提到了VBO,但是我不确定这是否适合我的情况,即每个对象的位置每一帧都会发生变化。
我目前正在使用OpenGL 1 - 我有工作代码和第3代/ 4 +设备(支持OpenGL 2,ha)的设备以合理的帧速率运行 - 但是在我的测试时(旧的,是的)第二代iPod touch,它非常缓慢,基本上无法播放。
我的代码包含“立方体”的静态顶点数组和包含每个立方体的位置和颜色的数组。我的游戏逻辑循环更新了数组中每个多维数据集的位置。目前我正在循环遍历立方体数组,为每个立方体调用glTranslatef和glDrawArrays。从我所看到的,这是非常低效的,但是我完全混淆了如何优化它。有什么想法吗?
(也许我不应该针对旧的,已停产的iOS设备,但鉴于我相信我的代码非常低效,我认为无论我是否找到解决此问题的方法,它都将有助于我未来的努力)
答案 0 :(得分:0)
您可以通过将所有立方体的所有坐标粘贴在单个阵列中并使用单个glDrawArrays调用绘制它来进行优化。
我不确定你为什么要将多维数据集拆分成单独的数组,除非因为它使你的数据结构更加优雅/面向对象,但这是我第一个看到改进的地方。
将多维数据集坐标转储到一个大数组中,并为每个多维数据集对象提供一个索引到该数组中,这样您仍然可以使更新逻辑保持相当划分(例如,多维数据集n拥有x到y范围内的坐标,并且负责更新它们,但是当您实际绘制坐标时,您可以直接在集中式坐标数组上运行glDrawArrays,而不是循环遍历立方体对象并单独渲染它们。
答案 1 :(得分:0)
对于这样的简单对象,我会制作一个大的VBO说200个对象* NrVerticesPerCube,将所有数据交错为Vertex,Normal,UV,Vertex,Normal,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 Verts,3 Normals和2UV),并将第一个关键帧复制到缓冲区,您可以对初始化做同样的事情对象位置,或者只是离开并计算后者......
然后在每个帧中,我为我的海狸的每个顶点在2个关键帧之间进行插值,并且只进行一次绘制调用,这对于我的海狸拥有的4029个顶点非常快,并且在我的iPhone 3G上以60FPS工作。
对于你只做gltranslate它会更简单,只需将x,y,z的值添加到每个立方体的每个顶点。
你会像这样更新它:
glBindBuffer(GL_ARRAY_BUFFER, vboObjects[vboGroupBeaver]);
GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
将vbo缓冲区和mapit绑定到缓冲区var。 在temp var上计算你想要的东西。
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]
};
所以这是交错的顶点,你得到顶点然后是法线然后是紫外线,如果你没有使用纹理替换紫外线的颜色。
最简单的方法是让一个包含所有对象的数组(如果你的所有对象都是相同的大小就很容易)并在绘制后进行位置更新(而不是在opengl框架的中间),更好的是一个单独的线程,创建2个VBO更新其中一个,同时从另一个绘图,如下所示:
这称为双缓冲,你使用它来保证稳定性,没有这个,有时你的游戏逻辑将更新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
这一切都是从内存中写的,所以可能是龙: - )
希望有所帮助。