C ++ OpenGL中的自遮挡感知多重几何混合(渲染)

时间:2017-07-23 23:05:39

标签: c++ opengl rendering mesh blending

我有各种姿势的相同复杂物体的几个网格(~100),旋转和平移参数略有不同。该物体由多个刚性部件组成,如手臂和腿部。

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

为了解决这个问题,我决定在OpenGL帧缓冲区中使用渲染,因为已知它们在计算上很便宜,并且因为我需要经常运行这个累积过程。

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

  • 腿的rgba(灰色,灰色,灰色,255),灰色= 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);

1 个答案:

答案 0 :(得分:0)

这是一种不同的方法:

创建两个帧缓冲区:normalaccnormal帧缓冲区应该有纹理存储(使用glFramebufferTexture2D)。

这是基本算法:

  1. acc清除为黑色
  2. 绑定normal,清除为黑色,渲染场景为白色腿,其他部分为黑色
  3. 绑定acc,渲染全屏矩形,上面有normal纹理,混合模式为GL_ONE,GL_ONE
  4. 转发动画,如果尚未完成,转到2。
  5. 您的结果为acc
  6. 因此,基本上,acc将包含各个帧的总和。