0
votes

I have a confusing situation in OpenGL 3.3 on the Mac. I have created an FBO with five attachment points sized at 512x512 apiece. I constructed a shader that writes to gl_FragData[0-4] for diffuse, normal, position, specular and emissive for my geometry. When I render the scene the back buffer AND the render targets are being updated even though I’ve only bound the FBO!

Here’s some code:

    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();
    }

And the FBO binder:

    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;
    }

I create the FBO with:

    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;
    }

And I go back to the back buffer with:

    void OpenGLESDriver::setDefaultTarget(){
      pushText( "setDefaultTarget" );
        glBindFramebuffer( GL_FRAMEBUFFER, 0 );//s_iFBOMaster );
        glAssert();
        glViewport( 0, 0, IEngine::cxView(), IEngine::cyView() );
        glAssert();
      popText();
    }

So the final rendering code looks like:

        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();

And for some reason the back buffer is being rendered to as well as the FBO. I must be missing something but haven’t got a clue what. Can anyone see what I’m doing wrong?

1
I'm actually a little confused as to why this GLenum aBuffers[max]; compiles when max is not a constant. I assume USE_MRT is undefined and it is the second branch of code in setFrameBufferAttachments that we should be focusing on? - Andon M. Coleman
In C++11 max doesn't need to be a constant. Admittedly it doesn't compile under Visual Studio but this code is for Android and OSX. That's ok because my Win32 renderer uses DirectX and my iOS one uses Metal. The Win32 code doesn't resort to such trickery. - toymaker

1 Answers

0
votes

After some poking around I finally found the answer. My renderer uses a vector of RenderNode objects which I fill up during rendering. I wasn't clearing that vector after I finished my post-effect. For some reason that unfilled vector was being rendered with the next FBO target, which drew all the first lot of geometry again in the second FBO. By clearing the vector at the beginning of the render pass and at the end I got rid of the problem. I'm still trying to track down who was committing all the render nodes again. Hopefully the code I pasted will help anyone who's looking to do FBOs because it does work after all. :)