【问题标题】:Self-occlusion-aware multiple geometry blending in C++ OpenGL (rendering)C++ OpenGL 中的自遮挡感知多几何混合(渲染)
【发布时间】:2017-07-23 23:05:39
【问题描述】:

我有几个网格(约 100 个)相同复杂对象的不同姿势,旋转和平移参数略有不同。该对象由多个刚性组件组成,例如手臂和腿。

目标是生成一张独特的灰度图片,显示特定身体部位的这些姿势的累积。获得的热图给出了身体部位可能的像素位置的概念,其中白色表示最大概率,黑色表示最小概率(越浅,概率越高)。说我对腿的堆积感兴趣。如果许多腿部姿势样本位于相同的 (x,y) 像素位置,那么我预计会在那里看到浅色像素。最终,腿部姿势可能不会完全重叠,因此我还希望看到腿部轮廓边界周围的黑色低概率平滑过渡。

为了解决这个任务,我决定在 OpenGL 帧缓冲区中使用渲染,因为众所周知这些缓冲区的计算成本很低,而且我需要经常运行这个累积过程。

我所做的是以下。我使用 GL_BLEND 在同一个帧缓冲区“fboLegsId”上累积了我感兴趣的身体部位的相应渲染(让我们仍然保留腿部示例)。为了区分腿 和身体的其余部分,我用两种颜色纹理网格:

  • rgba(gray,gray,gray,255) 用于腿部,其中 gray = 255 / 样本数 = 255/100

  • 身体其他部分的 rgba(0,0,0,0)

然后我通过执行以下操作来累积 100 个渲染(腿部的总和应为白色 = 255):

glBindFramebuffer(GL_FRAMEBUFFER, fboLegsId);

glClearColor(0,0,0,255);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

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

for each sample s = 0...100

   mesh.render(pose s);

end

glReadPixels(...)

这几乎符合我的预期。我确实获得了我想要的平滑灰度热图。但是存在自闭塞问题 即使我只使用 1 个样本也会出现这种情况。假设单个姿势样本,其中一条手臂在腿之前移动,部分遮挡它们。我希望在渲染过程中取消遮挡腿部的影响。然而,它呈现为好像手臂是不可见/半透明的,允许完全显示后面的像素。这会导致错误的渲染,从而导致错误的累积。

如果我简单地禁用混合,我会看到正确的自遮挡感知结果。所以,显然问题出在混合时间的某个地方。

我还尝试了不同的混合函数,到目前为止,以下一个产生了更接近自遮挡感知累积方法的结果: glBlendFunc(GL_ONE, GL_SRC_ALPHA); 无论如何,这里仍然存在一个问题:一个样本现在看起来是正确的;相反,两个或更多累积样本显示与其他样本重叠的伪影。如果像素不是腿的一部分,则看起来每个累积都会替换当前缓冲区像素。如果在(比方说)手臂前面多次发现腿,那么它会变得越来越暗,而不是越来越亮。 我尝试通过在启用深度计算的每次渲染迭代时清除深度缓冲区来解决此问题,但这并没有解决问题。

我觉得我的方法在概念上存在一些错误,或者某个地方存在小错误。


我已根据按预期执行的建议尝试了不同的方法。现在我正在使用 2 个帧缓冲区。第一个(SingleFBO)用于渲染具有正确自遮挡处理的单个样本。第二个(AccFBO)用于使​​用混合从第一个缓冲区累积 2D 纹理。请检查我下面的代码:

 // clear the accumulation buffer
 glBindFramebuffer(GL_FRAMEBUFFER, AccFBO);
 glClearColor(0.f, 0.f, 0.f, 1.f);
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 for each sample s = 0...100
 {
        // set rendering destination to SingleFBO
        glBindFramebuffer(GL_FRAMEBUFFER, SingleFBO);

        glClearColor(0.f, 0.f, 0.f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glEnable(GL_DEPTH_TEST);
        glDisable(GL_LIGHTING);
        mesh->render(pose s);
        glDisable(GL_DEPTH_TEST);
        glEnable(GL_LIGHTING);

        // set rendering destination to the accumulation buffer
        glBindFramebuffer(GL_FRAMEBUFFER, AccFBO);

        glClear(GL_DEPTH_BUFFER_BIT);

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

        // draw texture from previous buffer to a quad
        glBindTexture(GL_TEXTURE_2D, textureLeg);
        glEnable(GL_TEXTURE_2D);

        glDisable(GL_DEPTH_TEST);
        glDisable(GL_LIGHTING);
        glDepthMask(GL_FALSE);

        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();

        glBegin( GL_QUADS );
        {
            glTexCoord2f(0,0); glVertex2f(-1.0f, -1.0f);
            glTexCoord2f(1,0); glVertex2f(1.0f, -1.0f);
            glTexCoord2f(1,1); glVertex2f(1.0f, 1.0f);
            glTexCoord2f(0,1); glVertex2f(-1.0f, 1.0f);
        }
        glEnd();

        glPopMatrix();
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);

        // restore
        glDisable(GL_TEXTURE_2D);
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_LIGHTING);
        glDepthMask(GL_TRUE);

        glDisable(GL_BLEND);
  }

  glBindFramebuffer(GL_FRAMEBUFFER, AccFBO);
  glReadPixels(...)

请检查我的(标准)代码以初始化 SingleFBO(与 AccFBO 类似):

    // create a texture object
    glGenTextures(1, &textureLeg);
    glBindTexture(GL_TEXTURE_2D, textureLeg);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
                 GL_RGB, GL_UNSIGNED_BYTE, 0);
    glBindTexture(GL_TEXTURE_2D, 0);

    // create a renderbuffer object to store depth info
    glGenRenderbuffers(1, &rboLeg);
    glBindRenderbuffer(GL_RENDERBUFFER, rboLeg);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
                          width, height);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);

    // create a framebuffer object
    glGenFramebuffers(1, &SingleFBO);
    glBindFramebuffer(GL_FRAMEBUFFER, SingleFBO);

    // attach the texture to FBO color attachment point
    glFramebufferTexture2D(GL_FRAMEBUFFER,        // 1. fbo target: GL_FRAMEBUFFER 
                           GL_COLOR_ATTACHMENT0,  // 2. attachment point
                           GL_TEXTURE_2D,         // 3. tex target: GL_TEXTURE_2D
                           textureLeg,             // 4. tex ID
                           0);                    // 5. mipmap level: 0(base)

    // attach the renderbuffer to depth attachment point
    glFramebufferRenderbuffer(GL_FRAMEBUFFER,      // 1. fbo target: GL_FRAMEBUFFER
                              GL_DEPTH_ATTACHMENT, // 2. attachment point
                              GL_RENDERBUFFER,     // 3. rbo target: GL_RENDERBUFFER
                              rboLeg);              // 4. rbo ID

    // check FBO status
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if(status != GL_FRAMEBUFFER_COMPLETE)
        error(...);

    // switch back to window-system-provided framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

【问题讨论】:

  • 这是一种不同的方法:使用另一个(累积)帧缓冲区,它被初始化为黑色。到正常帧缓冲区,正常渲染角色,腿为白色,其他部分为黑色。然后,将此渲染用作纹理,将其添加到累积 fb 中。然后渲染下一个动画帧,并将其添加到累积 fb 中。我希望你明白我的意思
  • 我会尝试使用 stencil 吗?请参阅I have an OpenGL Tessellated Sphere and I want to cut a cylindrical hole in it 如果我没记错的话,您可以将其设置为增量,这正是您想要的。但我不确定如何通过 FBO 将模板返回到 CPU 端。
  • @geza:谢谢你的建议!您能否详细说明如何将内容从一个帧缓冲区传输到另一个帧缓冲区?这是否涉及 glReadPixels() 之间?这个操作计算量不是很大吗?
  • @Spektre:嗯,我在这方面的经验很少。与帧缓冲区相比,使用模板有什么优势?
  • 不,那会很慢。在创建“普通”帧缓冲区时使用 glFramebufferTexture2D,因此您也可以将此帧缓冲区用作纹理。在这种情况下,我无法查看驱动程序所做的事情,但这可能是免费操作。甚至,如果它不是免费的,它一定很便宜。

标签: c++ opengl rendering mesh blending


【解决方案1】:

这是一种不同的方法:

创建两个帧缓冲区:normal 和 acc。 normal 帧缓冲区应该有一个纹理存储(与glFramebufferTexture2D)。

这是基本算法:

  1. 将acc 清除为黑色
  2. 绑定normal,清除为黑色,将场景渲染为白色腿,其他部分为黑色
  3. 绑定acc,渲染一个全屏矩形,上面有normal纹理,混合模式GL_ONE,GL_ONE
  4. 转发动画,如果还没有完成,转到2。
  5. 您在acc 中有结果

所以,基本上,acc 将包含各个帧的总和。

【讨论】:

  • 感谢您的澄清!我已经用我正在使用的代码修改了我的问题。还是行不通。你能看一下,也许有一个我没发现的小错误?!
  • 循环前是否清除 AccFBO?我发现了一个问题:屏幕的 OpenGL 坐标系是 [-1:1]。所以你需要为glVertex2f 传递 [-1;1] 间隔。另一个问题是您将 2 GL_COLOR_ATTACHMENT0 附加到帧缓冲区。其中之一应该是深度的。据我从您的 cmets 了解到,rboLeg 应该是一个深度缓冲区。但是你使用 GL_RGB ,这是一个错误。也许还有其他问题:)
  • 它们实际上是“唯一”的问题。现在我的代码完美运行!非常感谢!我将使用工作代码编辑我的问题!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
  • 2011-08-08
  • 2010-11-21
  • 1970-01-01
  • 2011-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多