简体   繁体   中英

How to handle OpenGL additive blending and depth test with particles and deeper objects

I have a little sprites particle system made with OpenGL & glut using textures to draw a basic flame. The flame is reproduced symmetrically to illustrate how it behaves in a little box/scene. And as the pictures below demonstrate, there are two problems:

1- To produce a somewhat good looking flame effect I want to use additive blending with my particles, but the blending also takes the color of the deeper cyan panel into account and produce a white flame.

添加剂混合不适用于彩色背景

2.1 - Also, to achieve a correct implementation of the additive blending I have to disable the depth test while drawing the particles, but doing so enable the drawing of particles even if they should be "hidden".

深度测试

2.2 - If I enable the depth test while drawing the particles, here is what it looks like.

深度测试已启用

If it is of any help, here is the texture I am applying to the particles.

爆炸纹理

Here is the relevant code that displays the scene and the particles.

void drawParticles()
{
   glPushAttrib(GL_ALL_ATTRIB_BITS);
   glDisable(GL_LIGHTING);
   glDisable(GL_DEPTH_TEST);
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA,GL_ONE);
   glBindTexture(GL_TEXTURE_2D,explosionTexture[0]);
   glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);

   for (int i = 0; i < particlesNumber; ++i) 
   {
    glPointSize(50.0f);
        glBegin(GL_POINTS);
            glColor4f(particlesArray[i].color[0],particlesArray[i].color[1],particlesArray[i].color[2],0.5f);
            glVertex3f(particlesArray[i].position[0],particlesArray[i].position[1],particlesArray[i].position[2]);
       glEnd();
   }
   glBindTexture(GL_TEXTURE_2D, 0);
   glDisable( GL_BLEND );
   glEnable( GL_DEPTH_TEST );
   glPopAttrib();
}

void drawScene()
{
   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

   glMatrixMode( GL_PROJECTION );
   glLoadIdentity();
   gluPerspective( 60.0f, (GLdouble) g_width / (GLdouble) g_height, 0.1f, 300.0f );

   glMatrixMode( GL_MODELVIEW );
   glLoadIdentity();

   gluLookAt( dist*sin(phi)*sin(theta), dist*cos(phi), dist*sin(phi)*cos(theta), 0, 0, 0, 0, 1, 0 );

   glEnable( GL_DEPTH_TEST );
   glDisable( GL_BLEND );

   glBegin( GL_LINES );

      glColor3f( 1.0f, 0.0f, 0.0f );
      glVertex3f( 0.0f, 0.0f, 0.0f );
      glVertex3f( 0.5f, 0.0f, 0.0f );

      glColor3f( 0.0f, 1.0f, 0.0f );
      glVertex3f( 0.0f, 0.0f, 0.0f );
      glVertex3f( 0.0f, 0.5f, 0.0f );

      glColor3f( 0.0f, 0.0f, 1.0f );
      glVertex3f( 0.0f, 0.0f, 0.0f );
      glVertex3f( 0.0f, 0.0f, 0.5f );

   glEnd();

   glBegin( GL_QUADS );

      glColor4f( 1.0f, 1.0f, 1.0f , 0.0f);
      glVertex3f( -1.0f,  -0.2f,  1.0f );
      glVertex3f(  1.0f,  -0.2f,  1.0f );
      glVertex3f(  1.0f,  -0.2f, -1.0f );
      glVertex3f( -1.0f,  -0.2f, -1.0f );

      glColor4f( 1.0f, 1.0f, 0.0f , 0.0f );
      glVertex3f(  1.0f, -2.0f,  1.0f );
      glVertex3f(  1.0f, -2.0f, -1.0f );
      glVertex3f(  1.0f, -0.2f, -1.0f );
      glVertex3f(  1.0f, -0.2f,  1.0f );

      glColor4f( 1.0f, 0.0f, 1.0f , 0.0f );
      glVertex3f( -1.0f, -2.0f,  1.0f );
      glVertex3f( -1.0f, -2.0f, -1.0f );
      glVertex3f( -1.0f, -0.2f, -1.0f );
      glVertex3f( -1.0f, -0.2f,  1.0f );

      glColor4f( 0.0f, 1.0f, 1.0f , 1.0f );
      glVertex3f(  1.0f, -2.0f, -1.0f );
      glVertex3f( -1.0f, -2.0f, -1.0f );
      glVertex3f( -1.0f, -0.2f, -1.0f );
      glVertex3f(  1.0f, -0.2f, -1.0f );

   glEnd();

   glPushMatrix();
      drawParticles();
      glScalef(1.0f, -1.0f, 1.0f);
      drawParticles();
   glPopMatrix();


   glutSwapBuffers();
}

I am open to any kind of suggestions even involving shaders (but I would be interested to know if it is even possible to do with just plain OpenGL).

UPDATE :

Maybe I was unclear, I'm not necessarily interested in a strictly fixed-pipeline solution, I want to know how to manage additive blending in a scene even it means adding shaders code to my project.

Now, as Columbo pointed out, enabling the depth testing and disabling the depth writing solved my second problem. Now concerning the additive blending issue, I still have no clue about how to manage additive blending in a scene. Even though there might not such basic colors in a scene, the problem still remains as the flame will still be white and I'm open to know what I have to do with the pixel shader as suggested.

  1. For the additive blending issue, it may not be a problem. You'll never have a block of cyan in a real scene. However, if you really really need a solution, you could try a premultiplied alpha blend (glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);), then you have a bit more control. In your pixel shader you can multiply the output RGB by the source alpha manually, then you can choose the output alpha. An output alpha of zero produces an additive blend like you have at the moment. Outputting the full alpha value (vertex alpha * texture alpha) gives you a standard modulating alpha blend. You might be able to find some value in-between those two extremes which darkens the background enough to make your flame look yellow even against a cyan background without making it look rubbish. If you're not using pixel shaders, I believe it'd be possible with the fixed function pipeline by manipulating your texture during texture loading. It's all rather fiddly, and I'd suggest it's not worth doing, because you won't have such primary colours in a finished, lit scene. The more correct solution is to use HDR and tone mapping, but that's getting into some quite advanced rendering techniques.

  2. Fixing the depth problem is simple. You need to enable depth testing for your flame, but disable depth writing. glEnable(GL_DEPTH_TEST) and glDepthMask(GL_FALSE) are the relevant commands.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM