3

I have a simple particle effect in OpenGL using GL_POINTS. The following is called, being passed particles in order from the particle furthest from the camera first to the one nearest the camera last:

void draw_particle(particle* part) {
    /* The following is how the distance is calculated when ordering.
     * GLfloat distance = sqrt(pow(get_camera_pos_x() - part->pos_x, 2) + pow(get_camera_pos_y() - part->pos_y, 2) + pow(get_camera_pos_z() - part->pos_z, 2));
     */

    static GLfloat quadratic[] = {0.005, 0.01, 1/600.0};
    glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, quadratic);
    glPointSize(part->size);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_POINT_SPRITE_ARB);
    glEnable(GL_TEXTURE_2D);

    glBegin(GL_POINTS);
        glColor4f(part->r, part->g, part->b, part->a);
        glVertex3f(part->pos_x, part->pos_y, part->pos_z);
    glEnd();

    glDisable(GL_BLEND);
    glDisable(GL_POINT_SPRITE_ARB);
}

However, there is some artifacting when rendering as can be seen in the following effect:artifacted image http://img199.imageshack.us/img199/9574/particleeffect.png

The problems go away if I disable depth testing, but I need the effects to be able to interact with other elements of the scene, appearing in front of and behind elements of the same GL_TRIANGLE_STRIP depending on depth.

ICR
  • 13,896
  • 4
  • 50
  • 78
  • 1
    Just a comment about your code. I think you should lift out all non 'particle*' dependent code from the draw_particle() function: 1. Enable the things you want to use, set blend function, call glPointParameterfvARB etc 2. Call draw_particles(const std::vector& v); 3. Disable blend etc – rtn May 23 '09 at 17:10

2 Answers2

3

If your particules are already sorted you can render like this :

  • Render particule with GL WRITE DEPTH but no depth testing (I don't remember exactly the constants)
  • Render the rest of the scene with depth test.

This way you are sure to get scene interaction with nice-looking particules.

Raoul Supercopter
  • 5,076
  • 1
  • 34
  • 37
  • Managed to find the answer at the same time. The solution is to use: glDepthMask(GL_FALSE); [render particles] glDepthMask(GL_TRUE); – ICR May 23 '09 at 16:50
  • Other way around. You want to discard based on depth, but you don't want the blended primitives to affect each other by writing new depth. So the actual depth buffer contents is either only from glClear or the opaque objects. –  May 23 '09 at 16:51
2

Note: Please specify which OpenGL version you use when you post questions. That goes for any API. When you want to render primitives with alpha blending, you can't have depth writes enabled. You need to draw your blended primitives sorted back-to-front. If you have opaque objects in the scene, render them first, and then draw your transparent primitives in a back-to-front sorted fashion with depth test enabled and depth writes disabled.

glEnable(GL_DEPTH_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_POINT_SPRITE);
glEnable(GL_CULL_FACE);

while(1)
{
    ...
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDisable(GL_BLEND);
    glDepthMask(1);
    RenderOpaque();
    SortSprites();
    glEnable(GL_BLEND);
    glDepthMask(0);
    DrawSprites();
    ...
}