OpenGL FBO 与 MRT 写入后台缓冲区
Posted
技术标签:
【中文标题】OpenGL FBO 与 MRT 写入后台缓冲区【英文标题】:OpenGL FBO with MRT writing to back buffer 【发布时间】:2015-02-05 20:16:13 【问题描述】:我在 Mac 上的 OpenGL 3.3 中有一个令人困惑的情况。我创建了一个带有五个连接点的 FBO,每个连接点的大小为 512x512。我构建了一个着色器,它写入 gl_FragData[0-4] 用于我的几何体的漫反射、法线、位置、镜面反射和自发光。当我渲染场景时,后台缓冲区和渲染目标正在更新,即使我只绑定了 FBO!
这里有一些代码:
void OpenGLESDriver::setFrameBufferAttachments( u32 nAttachments, const u32* aAttachments )
pushText( "setFrameBufferAttachments" );
#if USE_MRT
GLint max;
glGetIntegerv( GL_MAX_DRAW_BUFFERS, &max );
GLenum aBuffers[max];
if( nAttachments > max )
nAttachments = max;
for( u32 i=0; i<nAttachments; ++i )
aBuffers[i] = GL_COLOR_ATTACHMENT0+aAttachments[i];
for( u32 i=nAttachments; i<max; ++i )
aBuffers[i] = GL_NONE;
glDrawBuffers( max, aBuffers );
glAssert();
#else
glDrawBuffer( GL_COLOR_ATTACHMENT0+aAttachments[0] );
glAssert();
#endif
popText();
还有 FBO 活页夹:
bool OpenGLESDriver::setFrameBuffer( const FrameBuffer::handle& hFrameBuffer )
if( hFrameBuffer )
pushText( "setFrameBuffer" );
glBindFramebuffer( GL_FRAMEBUFFER, hFrameBuffer->toFBO() );
glAssert();
if( !hFrameBuffer->toColorTargets().empty() )
u32 nAttachments = hFrameBuffer->toColorTargets().size();
u32 aAttachments[nAttachments];
for( u32 i=0; i<nAttachments; ++i )
aAttachments[i] = i;
setFrameBufferAttachments( nAttachments, aAttachments );
else
setFrameBufferAttachments( 0, 0 );
int w = hFrameBuffer->toDepthTexture()->toWidth();
int h = hFrameBuffer->toDepthTexture()->toHeight();
glViewport( 0, 0, w, h );
glAssert();
//clear out all texture stages because we don't want a left over
//frame buffer texture being bound to the shader.
for( u32 i=0; i<Material::kMaxSamplers; ++i )
setTextureStage( i, 0 );
popText();
return true;
return false;
我使用以下方式创建 FBO:
FrameBuffer::handle OpenGLESDriver::createFrameBuffer( const FrameBuffer::ColorTargets& vColorTargets, const DepthTarget::handle& hDT )
//--------------------------------------------------------------------
// Save off default FBO.
//--------------------------------------------------------------------
if( s_iFBOMaster < 0 )
glGetIntegerv( GL_FRAMEBUFFER_BINDING, &s_iFBOMaster );
glAssert();
//--------------------------------------------------------------------
// Generate frame buffer object.
//--------------------------------------------------------------------
GLuint fbo;
glGenFramebuffers( 1, &fbo );
glAssert();
glBindFramebuffer( GL_FRAMEBUFFER, fbo );
glAssert();
//--------------------------------------------------------------------
// Attach color RBO.
//--------------------------------------------------------------------
FrameBuffer::ColorTargets::const_iterator itCT = vColorTargets.getIterator();
u32 mrtIndex = 0;
while( itCT )
const ColorTarget::handle& hCT = itCT++;
if( !hCT )
continue;
if( hCT->toTexID() )
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0+mrtIndex,
GL_TEXTURE_2D,
hCT->toTexID(),
0 );
else if( hCT->toRBO() )
glFramebufferRenderbuffer(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0+mrtIndex,
GL_RENDERBUFFER,
hCT->toRBO() );
else
DEBUG_ASSERT_ALWAYS( "No color texture or RBO to attach!" );
glAssert();
++mrtIndex;
if( !checkFBStatus() )
e_log( "GL", "Couldn't create color attachment!" );
hCT.as<ColorTarget>()->toFlags()->bFailed = true;
//--------------------------------------------------------------------
// Attach depth RBO.
//--------------------------------------------------------------------
if( hDT )
if( hDT->toTexID() )
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D,
hDT->toTexID(),
0 );
else if( hDT->toRBO() )
glFramebufferRenderbuffer(
GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
hDT->toRBO() );
else
DEBUG_ASSERT_ALWAYS( "No depth texture or RBO to attach!" );
glAssert();
if( !checkFBStatus() )
e_log( "GL", "Couldn't create depth attachment!" );
hDT.as<DepthTarget>()->toFlags()->bFailed = true;
//--------------------------------------------------------------------
// New handle.
//--------------------------------------------------------------------
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glAssert();
FrameBuffer::handle hFrameBuffer = e_new( FrameBuffer );
hFrameBuffer->setColorTargets( vColorTargets );
hFrameBuffer->setDepthTarget( hDT );
hFrameBuffer->setFBO( u32( fbo ));
return hFrameBuffer;
然后我回到后台缓冲区:
void OpenGLESDriver::setDefaultTarget()
pushText( "setDefaultTarget" );
glBindFramebuffer( GL_FRAMEBUFFER, 0 );//s_iFBOMaster );
glAssert();
glViewport( 0, 0, IEngine::cxView(), IEngine::cyView() );
glAssert();
popText();
所以最终的渲染代码如下:
pushText( "Render MRT pass" );
if( setFrameBuffer( m_tPostFx.buffers[0] ))
setColorMask( true, true, true, true );
clearZ();
enableZBuffer( false );
setColor( color );
clearMRT( m_tPostFx.clearMRTShader );
enableZBuffer( true );
drawMRTPass();
popText();
由于某种原因,后台缓冲区和 FBO 一样被渲染。我一定错过了什么,但不知道是什么。谁能看到我做错了什么?
【问题讨论】:
我实际上有点困惑,为什么 thisGLenum aBuffers[max];
在 max
不是常量时编译。我假设USE_MRT
是未定义的,这是我们应该关注的setFrameBufferAttachments
中的第二个代码分支?
在 C++11 中,max 不需要是常数。诚然,它不能在 Visual Studio 下编译,但此代码适用于 android 和 OSX。没关系,因为我的 Win32 渲染器使用 DirectX,而我的 ios 渲染器使用 Metal。 Win32 代码不会使用这种诡计。
【参考方案1】:
经过一番摸索,我终于找到了答案。我的渲染器使用了我在渲染过程中填充的 RenderNode 对象向量。完成后期效果后,我没有清除该向量。由于某种原因,下一个 FBO 目标正在渲染未填充的矢量,这在第二个 FBO 中再次绘制了所有第一批几何图形。通过在渲染通道开始时清除矢量,最后我摆脱了这个问题。我仍在尝试追查谁再次提交了所有渲染节点。希望我粘贴的代码能帮助任何想要做 FBO 的人,因为它确实有效。 :)
【讨论】:
以上是关于OpenGL FBO 与 MRT 写入后台缓冲区的主要内容,如果未能解决你的问题,请参考以下文章