【问题标题】:OpenGL ignore object when blending?混合时OpenGL忽略对象?
【发布时间】:2019-09-10 16:49:01
【问题描述】:

我正在渲染一个带有轮廓的四边形。我通过将所需大小的四边形渲染为黑色来做到这一点,然后渲染“内部”四边形比第一个小。我还希望这个轮廓四边形是透明的。

我当前的尝试呈现如下:

但是我遇到的问题是,因为我实际上是在渲染两个四边形,所以黑色的“主”四边形会融合到较小的“内部”四边形。我不希望这种情况发生,因为我希望混合只混合背景和“内部”四边形颜色。

除了为轮廓渲染单独的四边形/三角形外,还有一种方法(理想情况下不使用着色器,因为我使用的是固定管道)在混合“内部”四边形时使混合忽略/不渲染“主”四边形?

相关代码:

混合模式:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable( GL_BLEND );

渲染盒子:

struct vertex
{
     GLfloat x,y;
};

void draw_box()
{
    glPushMatrix();

    glTranslatef( -3.0f, 1.12f, 1.0f );

    // Draw a huge black quad
    const vertex vertices2[] =
    {
        { -1.0f,  -1.0f  },
        {  0.0f,    0.0f },
        { -1.0f,    0.0f },

        { -1.0f,  -1.0f  },
        {  0.0f,    0.0f },
        {  0.0f,    -1.0f },

    };

    glVertexPointer(2, GL_FLOAT, 0, vertices2);
    glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    // Draw a smaller grey quad
    const GLfloat space = 0.04f;
    const vertex vertices3[] =
    {
        { -1.0f+space,  -1.0f+space  },
        {  0.0f-space,    0.0f-space },
        { -1.0f+space,    0.0f-space },

        { -1.0f+space,  -1.0f+space  },
        {  0.0f-space,    0.0f-space },
        {  0.0f-space,    -1.0f+space },

    };

    glVertexPointer(2, GL_FLOAT, 0, vertices3);
    glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    glPopMatrix();
}

【问题讨论】:

  • @Glorfindel:为什么要重新托管 TinyPic 占位符?
  • @genpfault 好问题。答案是my script 还不知道这个,它只知道this one。我会更新脚本,感谢关注!

标签: c++ opengl


【解决方案1】:

做到这一点的最佳方法可能是将边界分成四个梯形四边形,但由于您明确提到您不想走这条路,因此您可以采用其他几种选择。

下一个最好的方法可能是使用深度缓冲区:glEnable(GL_DEPTH_TEST)。首先绘制内部四边形(启用混合)。然后绘制外部四边形,但使用坐标使其深度大于第一个四边形(它“更远”)。如果在绘制边界四边形时启用了深度测试,则对于您已经绘制的四边形将失败,因为这些像素的深度值较小。请注意,使用此方法,如果您在渲染这些四边形后面的东西时写入深度缓冲区,您可能会遇到一些问题,即背景的某些部分被认为比新的四边形更近。您可以通过将深度测试配置为始终成功(即使测试失败)来解决内部四边形的问题:glDepthFunc(GL_ALWAYS)。但是,这对外部四边形没有帮助。在这种情况下,下一个方法可能会有所帮助:

您可以以与深度缓冲区类似的方式使用模板缓冲区。渲染内部四边形时,您将模板值(或至少其中的一位)设置为特定值:

glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, GLuint(-1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

然后在绘制外部四边形时,将模板测试配置为仅在片段不具有该值时才成功:

glStencilFunc(GL_NOT_EQUAL, 1, 1);

本质上,这与上面的版本对深度缓冲区所做的事情相同,但使用的是模板缓冲区。您可能需要查看glStencilFuncglStencilOp 的文档。

可能有其他方法可以实现此行为,但我能想到的所有其他解决方案都涉及使用自定义着色器,您表示这是不可接受的。就个人而言,我只是将边界分成 4 个四边形。当然,它比其他方法多 3 个四边形,但它不需要任何额外的 OpenGL 功能或状态更改。

【讨论】:

  • 我不确定我是否理解,所以深度缓冲区将免费剪掉外部四边形内部部分?使用这种方法会发生这种情况吗?
  • @paulm 是的,它基本上是免费的,除非您之前根本没有使用深度缓冲区,在这种情况下,创建具有深度缓冲区的上下文将使用大约两倍的内存用于帧缓冲区,因为每个像素(通常)需要 3 个字节用于颜色数据(4 个带 alpha)和(通常)3 个字节用于 24 位深度缓冲区(最常见),尽管 16 位深度缓冲区也很常见。
  • 我认为这仍然会产生与现在混合 2 个四边形的结果相同的糟糕结果,因为这与玻璃的效果相同?
  • 我不确定您所说的“与玻璃效果相同”是什么意思,但不,它看起来与上面不同,其中内部四边形被外部四边形变暗。当片段/像素的深度测试(或模板测试)失败时,该片段的颜色信息将被丢弃;不执行混合,帧缓冲区中的颜色也不会改变。