2
votes

I've combined all of my vertex data for many particles into a single array. How would I batch draw all of those particles in a manner that preserves their unique translations?

I am so confused as to how to do this. I've already created two posts on this issue but am still confused. I am currently using OpenGL ES 1.1 but am willing to upgrade to 2.0 if that means I can actually accomplish batch rendering of particles with unique translations, rotations, etc.

The two posts are:

Both explain a high level approach, but I need to know how to render this batch of particles where each particle's translation, rotation, and scale will change every frame. If your answer is to compute the translations on the CPU, then please show an example of this.

I am currently using Vertex Array Objects in my implementation. I undertstand I should be using VBO's for best performance. I will implement VBO's, but first I want to implement the batching. Once I finish the successful batching, I will change the VAO's to VBO's. Hence, the focus of this question is how to accomplish this with VAO's.

Here was my code before batching, where I would push a new matrix in the modelview matrix, translate, rotate, scale, and alpha according to the current Actor being rendered, and draw the vertices and textureCoords of the current Actor being rendered:

    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();

Now, given that I've combined the vertices and textureCoords of all particle Actors into batch arrays, I would like to push a new matrix in the modelview matrix, and draw the vertices and textureCoords of those Actors at their appropriate translations, rotations, scales, alphas, etc..

So my guess is it would look soemthing like:

    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();

A valid answer is one with a code example which clearly illustrates the solution to this question.

1

1 Answers

0
votes

Since all the particles have the same texture,this will do the work:

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;
    }
};

Your code:

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);
}

I cant test the code right now so if its working without a problem pls let me know

VBO's is useless in batch render cause you are uploading to the gpu new vertex data in every frame.