Как нарисовать более 1000 частиц (с уникальным вращением, масштабом и альфа) в системе частиц iPhone OpenGL ES без замедления игры?
Я разрабатываю игру для iPhone с использованием OpenGL ES 1.1. В этой игре у меня есть частицы крови, которые испускаются персонажами во время стрельбы, поэтому на экране одновременно может быть более 1000 частиц крови. Проблема в том, что когда мне нужно рендерить более 500 частиц, частота кадров в игре сильно падает.
В настоящее время каждая частица визуализируется с использованием glDrawArrays (..), и я знаю, что это является причиной замедления. Все частицы имеют один и тот же текстурный атлас.
Итак, что является лучшим вариантом, чтобы уменьшить замедление от рисования многих частиц? Вот варианты, которые я нашел:
- сгруппировать все частицы крови вместе и визуализировать их с помощью одного вызова glDrawArrays (..) - если я использую этот метод, есть ли способ для каждой частицы иметь свое собственное вращение и альфа? Или все они должны иметь одинаковое вращение при использовании этого метода? Если я не могу рендерить частицы с уникальным вращением, я не могу использовать эту опцию.
- Используйте точечные спрайты в OpenGL ES 2.0. Я пока не использую OpenGL ES 2.0, потому что мне нужно соблюдать крайний срок, который я должен был выпустить в 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, но частицы не могут иметь уникальное вращение, масштаб или альфа, используя этот метод (о котором я знаю).
// 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+ частиц без резкого падения частоты кадров, и каждая частица может иметь уникальное вращение, альфа и масштаб?
Любой конструктивный совет действительно поможет и будет очень признателен!
Спасибо!
2 ответа
С каждым вызовом OpenGL ES API возникают значительные накладные расходы, поэтому неудивительно, что вы видите здесь замедление с сотнями проходов в этом цикле рисования. Здесь вы найдете не только glDrawArrays(), но и отдельные вызовы glTranslatef(), glRotatef(), glScalef() и glColorf(). glDrawArrays() может показаться горячей точкой из-за способа, которым отложенный рендеринг работает на этих графических процессорах, но эти другие вызовы также повредят вам.
Вы должны сгруппировать эти вершины частиц в один массив (предпочтительно VBO, чтобы вы могли более эффективно передавать обновленные данные в графический процессор). Вы определенно можете повторить эффекты отдельного вращения, масштаба и т. Д. В вашем комбинированном массиве вершин, но вам нужно будет выполнить вычисления относительно того, где должны быть вершины, когда они вращаются, масштабируются и т. Д. Это приведет к некоторая нагрузка на ЦП для каждого кадра, но это может быть немного скомпенсировано с помощью инфраструктуры Accelerate для некоторой векторной обработки этого.
Цвет и альфа также могут быть предоставлены для каждой вершины в массиве, так что вы можете контролировать это для каждой из ваших частиц.
Тем не менее, я думаю, что вы правы в том, что OpenGL ES 2.0 может предоставить еще лучшее решение для этого, позволив вам написать специальную шейдерную программу. Вы можете отправлять статические вершины в VBO для всех ваших точек, тогда вам нужно будет только обновить матрицы, чтобы манипулировать каждой частицей и значениями альфа для каждой вершины частицы. Я делаю нечто подобное, чтобы генерировать процедурные самозванцы в качестве заменителей сфер Я опишу этот процесс здесь, и вы можете скачать исходный код приложения здесь.
Используйте около 1- 10 текстур, каждая из которых сделана, скажем, из 200 красных кровяных точек на прозрачном фоне, а затем нарисуйте их примерно 3 - 10 раз. Тогда у вас есть тысячи точек. Вы рисуете все изображения в виде шаров и т. Д. - взрываясь в слоях.
Вы не можете всегда делать 1: 1 переписку с реальностью в играх. Внимательно посмотрите на некоторые игры, которые работают на старых Xbox или iPad, и т. Д. - есть ярлыки, которые вы должны сделать - и они часто выглядят великолепно, когда закончите.