2

我正在使用 OpenGL ES 1.1 为 iPhone 开发游戏。在这个游戏中,我有角色在被射击时会发出的血颗粒,因此屏幕上随时可以有1000多个血颗粒。问题是当我要渲染超过 500 个粒子时,游戏的帧速率会急剧下降。

目前,每个粒子都使用glDrawArrays(..)渲染自己,我知道这是速度变慢的原因。所有粒子共享相同的纹理图集。

那么减少绘制许多粒子的速度的最佳选择是什么?以下是我找到的选项:

  1. 将所有血液粒子组合在一起,并使用一个glDrawArrays(..)调用来渲染它们——如果我使用这种方法,有没有办法让每个粒子都有自己的旋转和 alpha?或者当使用这种方法时,它们是否都必须具有相同的旋转?如果我不能用独特的旋转来渲染粒子,那么我就不能使用这个选项。
  2. 在 OpenGL ES 2.0 中使用点精灵。 我还没有使用 OpenGL ES 2.0,b/c 我需要在我设定的截止日期之前在 App Store 上发布我的游戏。使用 OpenGL ES 需要初步研究,遗憾的是我没有时间进行。我将在以后的版本中升级到 OpenGL ES 2.0,但首先,我只想使用 1.1。

这是每个粒子本身的渲染。这是我最初的粒子渲染方法,它导致游戏在渲染 500 多个粒子后帧速率显着下降。

// original method: each particle renders itself.
// slow when many particles must be rendered

[[AtlasLibrary sharedAtlasLibrary] ensureContainingTextureAtlasIsBoundInOpenGLES:self.containingAtlasKey];

glPushMatrix();

// translate
glTranslatef(translation.x, translation.y, translation.z);

// rotate
glRotatef(rotation.x, 1, 0, 0);
glRotatef(rotation.y, 0, 1, 0);
glRotatef(rotation.z, 0, 0, 1);

// scale
glScalef(scale.x, scale.y, scale.z);

// alpha
glColor4f(1.0, 1.0, 1.0, alpha);

// load vertices
glVertexPointer(2, GL_FLOAT, 0, texturedQuad.vertices);
glEnableClientState(GL_VERTEX_ARRAY);

// load uv coordinates for texture
glTexCoordPointer(2, GL_FLOAT, 0, texturedQuad.textureCoords);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

// render
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glPopMatrix();

然后我使用了方法 1,但粒子不能使用这种方法(我知道)具有唯一的旋转、缩放或 alpha。

    // this is method 1: group all particles and call glDrawArrays(..) once

    // declare vertex and uv-coordinate arrays
    int numParticles = 2000;
    CGFloat *vertices = (CGFloat *) malloc(2 * 6 * numParticles * sizeof(CGFloat));
    CGFloat *uvCoordinates = (CGFloat *) malloc (2 * 6 * numParticles * sizeof(CGFloat));

    ...build vertex arrays based on particle vertices and uv-coordinates.
    ...this part works fine.


    // get ready to render the particles
    glPushMatrix();
    glLoadIdentity();

    // if the particles' texture atlas is not already bound in OpenGL ES, then bind it
    [[AtlasLibrary sharedAtlasLibrary] ensureContainingTextureAtlasIsBoundInOpenGLES:((Particle *)[particles objectAtIndex:0]).containingAtlasKey];

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);

    glVertexPointer(2, GL_FLOAT, 0, vertices);
    glTexCoordPointer(2, GL_FLOAT, 0, uvCoordinates);

    // render
    glDrawArrays(GL_TRIANGLES, 0, vertexIndex);

    glPopMatrix();

我将重申我的问题:
如何渲染 1000 多个粒子而不会大幅降低帧速率,并且每个粒子仍然可以具有唯一的旋转、alpha 和缩放?

任何建设性的建议都会非常有帮助,我们将不胜感激!

谢谢!

4

2 回答 2

3

使用大约 1-10 个纹理,每个纹理由透明背景上的 200 个红血点组成,然后分别绘制大约 3-10 次。然后你有你的数千个点。您以球形图案等方式绘制所有图像 - 分层爆炸。

玩游戏时,您不能总是与现实进行一对一的对应。仔细看看一些在旧 Xbox 或 iPad 等上运行的游戏——你需要做一些捷径——完成后它们通常看起来很棒。

于 2011-09-15T19:47:44.767 回答
1

每个 OpenGL ES API 调用都有很大的开销,因此您在这里看到数百次通过该绘图循环时速度变慢也就不足为奇了。将您带到这里的不仅仅是 glDrawArrays(),还有各个 glTranslatef()、glRotatef()、glScalef() 和 glColorf() 调用。由于延迟渲染在这些 GPU 上的工作方式,glDrawArrays() 可能看起来是热点,但其他调用也会对您造成伤害。

您应该将这些粒子顶点组合在一个数组中(最好是 VBO,以便您可以更有效地利用流式更新数据到 GPU)。您绝对可以在组合的顶点数组中复制单个旋转、缩放等的效果,但是您需要执行顶点在旋转、缩放等时的位置的计算。这将放置每一帧都会给 CPU 带来一些负担,但可以通过使用 Accelerate 框架对此进行一些矢量处理来抵消一些负担。

颜色和 alpha 也可以为数组中的每个顶点提供,因此您可以为每个粒子控制它。

但是,我认为您是对的,OpenGL ES 2.0 可以通过让您编写自定义着色器程序来为此提供更好的解决方案。您可以在 VBO 中为所有点发送静态顶点,然后只需要更新矩阵来操纵每个粒子和每个粒子顶点的 alpha 值。我做了类似的事情来生成程序冒名顶替者作为球体的替身。我在这里描述了这个过程,您可以在这里下载应用程序的源代码。

于 2011-09-15T23:19:12.970 回答