OpenGL ES 2.0 - Как пакетно рисовать частицы, которые имеют уникальные перемещения, вращения, масштабы и альфы?
Я объединил все свои данные вершин для многих частиц в один массив. Как бы я нарисовал все эти частицы так, чтобы сохранить их уникальные переводы?
Я так растерялся, как это сделать. Я уже создал два сообщения по этому вопросу, но все еще в замешательстве. В настоящее время я использую OpenGL ES 1.1, но готов перейти на 2.0, если это означает, что я действительно могу выполнить пакетную визуализацию частиц с уникальными переводами, вращениями и т. Д.
Два сообщения:
- OpenGL ES - Как Batch Render 500+ частиц с различными альфа, вращениями и масштабами?
- Как нарисовать более 1000 частиц (с уникальным вращением, масштабом и альфа) в системе частиц iPhone OpenGL ES без замедления игры?
Оба объясняют подход высокого уровня, но мне нужно знать, как визуализировать этот пакет частиц, в котором перемещение, вращение и масштаб каждой частицы будут меняться в каждом кадре. Если ваш ответ состоит в том, чтобы вычислить переводы на CPU, то, пожалуйста, покажите пример этого.
В настоящее время я использую Vertex Array Objects в моей реализации. Я понимаю, что я должен использовать VBO для лучшей производительности. Я буду реализовывать VBO, но сначала я хочу реализовать пакетирование. Как только я закончу успешную дозировку, я заменю VAO на VBO. Следовательно, основное внимание в этом вопросе уделяется тому, как этого добиться с помощью ВАО.
Вот мой код до пакетной обработки, где я вставлял новую матрицу в матрицу вида модели, переводил, вращал, масштабировал и альфа в соответствии с текущим отображаемым актором и рисовал вершины и textureCoords текущего отображаемого актера:
glPushMatrix();
glTranslatef(translation.x, translation.y, translation.z);
// rotation
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);
// color and alpha
glColor4f(1.0, 1.0, 1.0, alpha);
glVertexPointer(2, GL_FLOAT, 0, aSprite.vertices);
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, texturedQuad.textureCoords);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glPopMatrix();
Теперь, учитывая, что я объединил вершины и текстурные координаты всех акторов частиц в пакетные массивы, я хотел бы вставить новую матрицу в матрицу вида модели и нарисовать вершины и текстурные корни этих акторов в соответствующих переводах, поворотах и масштабах. Альфы и т.д..
Так что я думаю, это будет выглядеть примерно так:
static GLFloat verticesBatched[appropriate_length] = ...; // I have a method to populate this array based on the Actors to render
static GLFloat textureCoordsBatched[appropriate_length] = ...; // I have a method to populate this array based on the Actors to render
glPushMatrix();
// perform CPU matrix manipulation to manually translate, rotate, and scale all vertices
glVertexPointer(2, GL_FLOAT, 0, verticesBatched);
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, textureCoordsBatched);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glPopMatrix();
Правильный ответ - пример с примером кода, который ясно иллюстрирует решение этого вопроса.
1 ответ
Поскольку все частицы имеют одинаковую текстуру, это сделает работу:
float squarevData[12]={
-1,1,
1,1,
-1,-1,
1,1,
1,-1,
-1,-1,
};
float squarevDataSteady[12]={
-1,1,
1,1,
-1,-1,
1,1,
1,-1,
-1,-1,
};
class BatchRenderer
{
public:
float* partVdata;
float* partCdata;
float* partTdata;
bool isanydrawed;
int counter1,counter2,counter3;
int count;
bool isz;
bool textured;
void Innit(int maxTextures,bool iszi)
{
isz=iszi;
if(isz)partVdata=(float*)malloc(maxTextures*18*4);
else partVdata=(float*)malloc(maxTextures*12*4);
partCdata=(float*)malloc(maxTextures*24*4);
partTdata=(float*)malloc(maxTextures*12*4);
isanydrawed=false;
}
void Draw(float x,float y,float z,float scalex,float scaley,float angle,float r,float g,float b,float a)
{
isanydrawed=true;
angle*=0.017453f;
for(int c2=0;c2<12;c2+=2)
{
float x=squarevData[c2]*scalex;
float y=squarevData[c2+1]*scaley;
float cos1=cos(angle);
float sin1=sin(angle);
squarevDataSteady[c2] = (cos1*x) - ( sin1*y);
squarevDataSteady[c2+1] = (sin1*x) + ( cos1*y);
}
partVdata[counter1++]=x+squarevDataSteady[0];
partVdata[counter1++]=y+squarevDataSteady[1];
if(isz)partVdata[counter1++]=z;
partCdata[counter2++]=r;
partCdata[counter2++]=g;
partCdata[counter2++]=b;
partCdata[counter2++]=a;
partTdata[counter3++]=0;
partTdata[counter3++]=1;
partVdata[counter1++]=x+squarevDataSteady[2];
partVdata[counter1++]=y+squarevDataSteady[3];
if(isz)partVdata[counter1++]=z;
partCdata[counter2++]=r;
partCdata[counter2++]=g;
partCdata[counter2++]=b;
partCdata[counter2++]=a;
partTdata[counter3++]=1;
partTdata[counter3++]=1;
partVdata[counter1++]=x+squarevDataSteady[4];
partVdata[counter1++]=y+squarevDataSteady[5];
if(isz)partVdata[counter1++]=z;
partCdata[counter2++]=r;
partCdata[counter2++]=g;
partCdata[counter2++]=b;
partCdata[counter2++]=a;
partTdata[counter3++]=0;
partTdata[counter3++]=0;
partVdata[counter1++]=x+squarevDataSteady[6];
partVdata[counter1++]=y+squarevDataSteady[7];
if(isz)partVdata[counter1++]=z;
partCdata[counter2++]=r;
partCdata[counter2++]=g;
partCdata[counter2++]=b;
partCdata[counter2++]=a;
partTdata[counter3++]=1;
partTdata[counter3++]=1;
partVdata[counter1++]=x+squarevDataSteady[8];
partVdata[counter1++]=y+squarevDataSteady[9];
if(isz)partVdata[counter1++]=z;
partCdata[counter2++]=r;
partCdata[counter2++]=g;
partCdata[counter2++]=b;
partCdata[counter2++]=a;
partTdata[counter3++]=1;
partTdata[counter3++]=0;
partVdata[counter1++]=x+squarevDataSteady[10];
partVdata[counter1++]=y+squarevDataSteady[11];
if(isz)partVdata[counter1++]=z;
partCdata[counter2++]=r;
partCdata[counter2++]=g;
partCdata[counter2++]=b;
partCdata[counter2++]=a;
partTdata[counter3++]=0;
partTdata[counter3++]=0;
count++;
}
void RenderStart()
{
counter1=counter2=count=counter3=0;
}
void RenderStop(int textureid)
{
if(!isanydrawed)return;
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glBindTexture(GL_TEXTURE_2D, textureid);
glTexCoordPointer(2, GL_FLOAT, 0, partTdata);
glColorPointer(4, GL_FLOAT, 0,partCdata );
if(isz)glVertexPointer(3, GL_FLOAT, 0, partVdata);
else glVertexPointer(2, GL_FLOAT, 0, partVdata);
glDrawArrays(GL_TRIANGLES, 0, count*6);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
isanydrawed=false;
}
};
Ваш код:
BatchRenderer* br=new BatchRenderer(MAX_TEXTURES_NUM,true);//true since you are drawing 3d
void onParticlesRender()
{
br->RenderStart();
for(int c=0;c<PARTICLES_SIZE;c++)
{
br->Draw(p[c].pos.x,p[c].pos.y,p[c].pos.z,p[c].scale.x,p[c].scale.y,p[c].angle,p[c].r,p[c].g,p[c].b,p[c].a);
}
br->RenderStop(yourTextureID);
}
Я не могу проверить код прямо сейчас, так что если он работает без проблем, пожалуйста, дайте мне знать
VBO бесполезен в пакетном рендеринге, потому что вы загружаете в gpu новые данные вершин в каждом кадре.