C++ C+中的自遮挡多几何体融合+;OpenGL(渲染)
我已经得到了同一个复杂对象的多个网格(~100),在不同的姿势下,旋转和平移参数略有不同。该对象由多个刚性组件(如手臂和腿)组成 目标是生成一张独特的灰度图片,显示特定身体部位的这些姿势的累积。获得的热图给出了身体部位可能像素位置的概念,其中白色表示最大概率,黑色表示最小概率(越轻概率越高)。说我对腿的积累感兴趣。如果多个腿部姿势样本位于同一(x,y)像素位置,则我预计会在那里看到灯光像素。最终,腿部姿势可能不会完全重叠,因此我还希望在腿部轮廓边界周围看到平滑过渡到黑色的低概率 为了解决这个问题,我决定在OpenGL帧缓冲区中使用渲染,因为它们在计算上非常便宜,而且我需要经常运行这个累积过程 我所做的如下。我使用GL_BLEND在同一帧缓冲区“fboLegsId”上累积我感兴趣的身体部位的相应渲染(让我们仍然保留腿部示例)。为了区分腿 身体的其余部分,我用两种颜色对网格进行纹理处理:C++ C+中的自遮挡多几何体融合+;OpenGL(渲染),c++,opengl,rendering,mesh,blending,C++,Opengl,Rendering,Mesh,Blending,我已经得到了同一个复杂对象的多个网格(~100),在不同的姿势下,旋转和平移参数略有不同。该对象由多个刚性组件(如手臂和腿)组成 目标是生成一张独特的灰度图片,显示特定身体部位的这些姿势的累积。获得的热图给出了身体部位可能像素位置的概念,其中白色表示最大概率,黑色表示最小概率(越轻概率越高)。说我对腿的积累感兴趣。如果多个腿部姿势样本位于同一(x,y)像素位置,则我预计会在那里看到灯光像素。最终,腿部姿势可能不会完全重叠,因此我还希望在腿部轮廓边界周围看到平滑过渡到黑色的低概率 为了解决这个问题
- 腿的rgba(灰色,灰色,灰色,255),其中灰色=255/样本数=255/100
- 身体其他部位的rgba(0,0,0,0)
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(...)
这几乎和我预期的一样。我确实得到了我想要的平滑灰度热图。但也存在自遮挡问题
即使我只使用一个样本,也会出现这种情况。假设对于单个姿势样本,其中一只手臂在腿部之前移动,部分遮挡它们。我希望在渲染过程中取消遮挡腿部部分的影响。但是,它会渲染为手臂不可见/半透明,使后面的像素完全显示出来。这会导致错误的渲染,从而导致错误的累积
如果我简单地禁用混合,我会看到正确的自我遮挡感知结果。所以,很明显,问题出在混合时间的某个地方
我还尝试了不同的混合功能,到目前为止,以下功能产生了与自我遮挡感知累积方法更接近的结果:
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的类似代码):
这里有一种不同的方法: 创建两个帧缓冲区:
normal
和acc
<代码>普通帧缓冲区应具有纹理存储(使用glFramebufferTexture2D
)
以下是基本算法:
法线
,清除为黑色,并使用白色支腿渲染场景,其他部分渲染为黑色acc
,渲染一个全屏矩形,上面有normal
纹理,使用混合模式GL_ONE,GL_ONEacc
因此,基本上,
acc
将包含单个帧的总和。这里有一种不同的方法:使用另一个(累积)帧缓冲区,初始化为黑色。对于普通帧缓冲区,正常渲染角色,腿部为白色,其他部分为黑色。然后,将此渲染用作纹理,将其添加到纹理中。然后渲染下一个动画帧,并将其添加到累积fb。我希望你明白我的意思我会尝试使用模具?看看我是否记得正确,你可以将它设置为增量,这正是你想要的。但我不知道如何通过FBO将模具返回CPU端。@geza:谢谢你的建议!你能详细说明一下如何将内容从一个帧缓冲区传输到另一个帧缓冲区吗?这是否涉及中间的glReadPixels()?这个运算在计算上不是很昂贵吗?@Spektre:嗯,我在这方面的经验很少。在帧缓冲区上使用模具的优点是什么?不,那会很慢。创建“普通”帧缓冲区时使用glFramebufferTexture2D,因此您也可以将此帧缓冲区用作纹理。我无法了解驾驶员在这种情况下的行为,但这可能是一种自由操作。甚至,如果不是免费的,一定很便宜。谢谢你的澄清!我已经用我正在使用的代码修改了我的问题。仍然不起作用。你能看一看吗,也许在某个我看不见的地方有个小错误?!循环前是否清除AccFBO?我发现了一个问题:屏幕上的OpenGL坐标系是[-1:1]。因此,您需要为glVertex2f
传递[-1;1]间隔。另一个问题是您附加了
// 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);