Jump to content
  • Advertisement
Sign in to follow this  
medevilenemy

Speeding up particle effect

This topic is 3801 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am working on particle emitters for engine exhaust for the game I'm making, and I really need to optimize it. Right now it is taking a framerate cost of about 20fps. I would like to minimize that as much as possible. Basically, its a large number of triangular planar particles. Every update, their alpha level drops slightly (to create a fade-with-distance effect) and they move slightly backwards on the z axis. Is there a way to put particle drawing into a display list or something so that it draws faster (to the point of relatively minimal framerate cost)? I've used display lists before, but on stationary effects where nothing ever changes. here is the code to my engine-emitter subsystem:
class REPart // Rectangular engine particle
{
    public:
        void InitParticle(float xmin, float xmax, float ymin, float ymax, float zc, float r, float g, float b);
        void DrawParticle(void);
        bool IsVisible(void);
    private:
        float x; // Horizontal Location
        float y; // Vertical Location
        float z;
        float red;
        float green;
        float blue;
        float alpha;
};
void REPart::InitParticle(float xmin, float xmax, float ymin, float ymax, float zc, float r, float g, float b)
{

    float width = xmax - xmin;
    width = fabs(width);
    float half = (width / 2.0);

    float height = ymax - ymin;
    height = fabs(height);
    float hhalf = (height / 2.0);

    x = (((float)rand()/(float)RAND_MAX))*(xmin);
    x+= (((float)rand()/(float)RAND_MAX))*(xmax);
    y = (((float)rand()/(float)RAND_MAX))*(ymin);
    y+= (((float)rand()/(float)RAND_MAX))*(ymax);
    z = zc;

    red = r;
    green = g;
    blue = b;
    alpha = (((float)rand()/(float)RAND_MAX))*(1.0);
    //alpha  = 1.0-(fabs(x)/half);
    //alpha -= (fabs(y)/half);
}

void REPart::DrawParticle(void)
{
    float size = 0.001;
    glColor4f(red, green, blue, alpha);
    glBegin(GL_TRIANGLES);
        glVertex3d(x, y, z);
        glVertex3d(x+size, y, z);
        glVertex3d(x+size, y+size, z);
    glEnd();

    z+=0.006;
    alpha-=0.01;
}
bool REPart::IsVisible(void)
{
    if(alpha > 0)
         return true;
    if(alpha <= 0)
         return false;
    return true;
}
class Engine : public Rotatable
{
    public:
    Engine()
    {
    };
    ~Engine()
    {
    };
    virtual void Update()
    {
    }

    protected:
    float dx; // X location relative to parent
    float dy; // Y location relative to parent
    float dz; // Z location relative to parent
    float pitch; // Pitch relative to parent (rotation about parent's X axis
    float yaw; // Yaw relative to parent (rotation about parent's Y axis
    float r;
    float g;
    float b;
    float wid;
    float len;


    Rotatable *parent; // A reference to the parent ship, to facilitate easier updates.

    //etc
};
class Rect_Engine : public Engine
{
    public:
    Rect_Engine()
    {
    };
    Rect_Engine(Rotatable *par, float dispx, float dispy, float dispz, float length, float width, float rx, float ry, float red, float green, float blue)
    {
        int i;

        dx = dispx;
        dy = dispy;
        dz = dispz;
        pitch = rx;
        yaw = ry;
        wid = width;
        len = length;
        r = red;
        g = green;
        b = blue;

        parent = par;
        Position = parent->GetPos();
        UpVector = parent->GetUp();
        RightVector = parent->GetRight();
        ViewDir = parent->GetView();


        MoveForward(-dz);
        StrafeRight(dx);
        MoveUpward(dy);
        RotateX(pitch);
        RotateY(yaw);

        for(i=0; i<MAX_ENGINE_PARTS; i++)
        {
            parts.InitParticle(dispx, width, dispy, length, dispz, red, green, blue);
        }

    }
    void Update() // Updates position
    {
        int i;

        Position = parent->GetPos();
        UpVector = parent->GetUp();
        RightVector = parent->GetRight();
        ViewDir = parent->GetView();

        MoveForward(-dz);
        StrafeRight(dx);
        MoveUpward(dy);
        RotateX(pitch);
        RotateY(yaw);

        for(i=0; i<MAX_ENGINE_PARTS; i++)
        {
            if(parts.IsVisible() == false)
                parts.InitParticle(dx, wid, dy, len, dz, r, g, b);
            else
                parts.DrawParticle();
        }

        // CODE FOR DRAWING THE BLOODY THING HERE
    }
    private:
    REPart parts[MAX_ENGINE_PARTS];
};






Thanks for your time, folks! [edit] I know the code there would make it very ugly. This is basically the first revision of it. I'm working on an algorithm to make the particle placement and coloring look better. [edit#2] Sorry, I just noticed I forgot the REPart definition. It has been added. [Edited by - medevilenemy on July 18, 2008 12:23:07 AM]

Share this post


Link to post
Share on other sites
Advertisement
Don't use immediate mode (glBegin) and certainly don't draw your particles one at a time. Put them all into a big array and draw them via vertex arrays or VBOs.

Share this post


Link to post
Share on other sites
I don't know how to do that. I've tried to figure out vertex arrays/VBOs before... but without any success. Could someone show a modification of the code that would work? Thanks for commenting.

Share this post


Link to post
Share on other sites
For vertex arrays, you'd want to do something like...


float Triangle[3 * 3] =
{
-0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f,
0.5f, 0.0f, 0.0f,
};

// http://opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/enableclientstate.html
// void glEnableClientState(GLenum cap)
glEnableClientState(GL_VERTEX_ARRAY);

/* You are probably going to want to enable GL_TEXTURE_COORD_ARRAY and GL_NORMAL_ARRAY. */

// http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/vertexpointer.html
// void glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer )
glVertexPointer(3, GL_FLOAT, 0, Triangle);

/* There is also glNormalPointer() and glTexCoordPointer() */
// glTexCoordPointer(): http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/texcoordpointer.html
// glNormalPointer(): http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/normalpointer.html

// http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/drawarrays.html
// void glDrawArrays(GLenum mode, GLint first, GLsizei count)
glDrawArrays(GL_TRIANGLE, 0, 1); // count is number of primitives, not vertices




I hope that helps.

Share this post


Link to post
Share on other sites
Did you read my post above? The sample and the links in the comments will tell you everything you need to know.

Share this post


Link to post
Share on other sites
Another thing you can try which can give a great performance increase is point sprites. It essentially mimics having a textured quad as a particle but you need only supply a point.

check out the GL_ARB_point_sprite extension.

here is some sample code I took from a particle system I wrote back in the day.


const float quadratic[] = { 1.0f, 0.0f, 0.01f };

glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, quadratic);
glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 60.0f);
glPointParameterfARB(GL_POINT_SIZE_MIN_ARB, 1.0f);
glPointParameterfARB(GL_POINT_SIZE_MAX_ARB, MaxPointSize);

glTexEnvf(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);

glEnable(GL_POINT_SPRITE_ARB);
glPointSize( size );

glBegin(GL_POINTS);

for(all particles)
{
glColor3f(particle->color.x, particle->color.y, particle->;color.z);
glVertex3f(particle->position.x,particle->position.y,particle->position.z);
}

glEnd();

glDisable(GL_POINT_SPRITE_ARB);




Share this post


Link to post
Share on other sites
You could probably improve your performance a bunch just by putting your glBegin() just before the "for(i=0; i<MAX_ENGINE_PARTS; i++)" and the glEnd() after the loop, instead of having them in your DrawParticle function.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!