如何使用粒子和更深的对象处理 OpenGL 加法混合和深度测试

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

我有一个用 OpenGL 和 glut 使用纹理绘制基本火焰的小精灵粒子系统。火焰被对称地再现,以说明它在 box/scene 中的表现。如下图所示,存在两个问题:

1- 为了产生稍微好看的火焰效果,我想对我的粒子使用加法混合,但混合也考虑了更深的青色面板的颜色并产生白色火焰。

2.1 - 此外,为了正确实现加法混合,我必须在绘制粒子时禁用深度测试,但这样做可以启用粒子绘制,即使它们应该是 "hidden"。

2.2 - 如果我在绘制粒子时启用深度测试,这就是它的样子。

如果有任何帮助,这是我应用到粒子的纹理。

这里是显示场景和粒子的相关代码。

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

我愿意接受任何类型的建议,甚至涉及着色器(但我很想知道是否可以只使用普通的 OpenGL)。

更新

也许我不清楚,我不一定对严格固定的管道解决方案感兴趣,我想知道如何在场景中管理添加混合,即使这意味着将着色器代码添加到我的项目中。

现在,正如 Columbo 指出的那样,启用深度测试并禁用深度写入解决了我的第二个问题。现在关于添加剂混合问题,我仍然不知道如何在场景中管理添加剂混合。即使场景中可能没有这样的基本颜色,问题仍然存在,因为火焰仍然是白色的,我愿意知道我必须按照建议对像素着色器做什么。

  1. 对于添加剂混合问题,可能不是问题。在真实场景中你永远不会有一块青色。然而,如果你真的真的需要一个解决方案,你可以尝试预乘 alpha 混合 (glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);),然后你有更多的控制权。在您的像素着色器中,您可以手动将输出 RGB 乘以源 alpha,然后您可以选择输出 alpha。零输出 alpha 会产生像您现在这样的加法混合。输出完整的 alpha 值(顶点 alpha * 纹理 alpha)为您提供标准的调制 alpha 混合。您也许可以在这两个极端之间找到一些值,使背景变暗,足以使您的火焰即使在青色背景下也看起来是黄色的,而不会使它看起来很垃圾。如果您不使用像素着色器,我相信可以通过在纹理加载期间操纵纹理来使用固定功能管道。这一切都相当繁琐,我建议不值得这样做,因为在完成的、有光照的场景中不会有这样的原色。更正确的解决方案是使用 HDR 和色调映射,但这涉及到一些相当高级的渲染技术。

  2. 解决深度问题很简单。您需要为您的火焰启用深度测试,但禁用深度写入。 glEnable(GL_DEPTH_TEST) 和 glDepthMask(GL_FALSE) 是相关命令。