# FBO Drawing App

**URL:** <https://discourse.libcinder.org/t/fbo-drawing-app/1585>\
**Category:** Using Cinder\
**Created:** [November 12, 2019, 2:22am UTC](https://discourse.libcinder.org/t/fbo-drawing-app/1585 "2019-11-12T02:22:16Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Renderman](https://avatars.discourse-cdn.com/v4/letter/r/dbc845/32.png) [@Renderman](https://discourse.libcinder.org/u/Renderman)\
**Post date:** [November 12, 2019, 2:22am UTC](https://discourse.libcinder.org/t/fbo-drawing-app/1585/1 "2019-11-12T02:22:17Z")

</div>

I’m trying to create a simple drawing app with an FBO.  
resetFbo() is supposed to fill the FBO with green.  
When mouse is down, a circle brush is supposed to draw into FBO.  
When I draw, the green background disappears (becomes black), the brush strokes stay.  
Can anyone tell me what I’m doing wrong?  
Thanks for your help

```
#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/Camera.h"
#include "cinder/gl/gl.h"
#include "cinder/ImageIo.h"
#include "cinder/Utilities.h"
#include "cinder/params/Params.h"
#include "Resources.h"

#define SIZE 512

using namespace ci;
using namespace ci::app;
using namespace std;

class DrawingCanvasApp : public App {
  public:
    static void prepareSettings( Settings *settings );
    void setup() override;
    void update() override;
    void draw() override;
    void mouseMove( MouseEvent event ) override;
    void mouseDown( MouseEvent event ) override;
    void mouseDrag( MouseEvent event ) override;
    void mouseUp( MouseEvent event ) override;
  private:
    void resetFbo();
            
    gl::FboRef mFBO;
    
    vec2 mMouse;
    bool mMousePressed;
    
    static const int FBO_WIDTH = SIZE, FBO_HEIGHT = SIZE;
};

void DrawingCanvasApp::prepareSettings( Settings *settings )
{
    settings->setWindowSize( SIZE, SIZE );
    settings->setFrameRate( 60.0f );
}

void DrawingCanvasApp::mouseDown( MouseEvent event )
{
    mMousePressed = true;
}

void DrawingCanvasApp::mouseUp( MouseEvent event )
{
    mMousePressed = false;
}

void DrawingCanvasApp::mouseMove( MouseEvent event )
{
    mMouse = event.getPos();
}

void DrawingCanvasApp::mouseDrag( MouseEvent event )
{
    mMouse = event.getPos();
}

void DrawingCanvasApp::setup()
{
    mMousePressed = false;
    
    mFBO = gl::Fbo::create( FBO_WIDTH, FBO_HEIGHT, gl::Fbo::Format().colorTexture() );
    resetFbo();
}

void DrawingCanvasApp::update()
{
    gl::setMatricesWindow( mFBO->getSize() );
    gl::viewport( mFBO->getSize() );
    
    mFBO->bindFramebuffer();
    
    gl::enableAlphaBlending();
    
    if( mMousePressed )
    {
        ci::gl::color( ColorA( 1, 0, 0, 1 ) );
        gl::drawSolidCircle( mMouse, 25.0f, 32 );
    }
    
    mFBO->unbindFramebuffer();
}

void DrawingApp::draw()
{
    gl::clear( Color( 0.0, 0.0, 1.0 ) );
    gl::enableAlphaBlending();
    gl::setMatricesWindow( getWindowSize() );
    gl::viewport( getWindowSize() );
    
    gl::draw( mFBO->getColorTexture() );
}

void DrawingCanvasApp::resetFbo()
{
    gl::setMatricesWindow( mFBO->getSize() );
    gl::viewport( mFBO->getSize() );
    
    mFBO->bindFramebuffer();
    
    gl::enableAlphaBlending();
            
    ci::gl::color( ColorA( 0, 1, 0, 1 ) );
    gl::drawSolidRect( mFBO->getBounds() );
    
    mFBO->unbindFramebuffer();
}

CINDER_APP( DrawingCanvasApp, RendererGl, DrawingCanvasApp::prepareSettings )
```

---

<div class="post-metadata">

**Author:** ![lithium](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/lithium/32/38_2.png) [@lithium](https://discourse.libcinder.org/u/lithium)\
**Post date:** [November 12, 2019, 4:08am UTC](https://discourse.libcinder.org/t/fbo-drawing-app/1585/2 "2019-11-12T04:08:36Z")

</div>

Here’s a couple of changes that will fix the problem you’re seeing. The main problem you had was that you weren’t being mindful of OpenGL being one big state machine. Your call to `gl::color` to draw the red circle persists until another call is made to override it, which meant you were blitting that FBO to the screen modulated with red.

For this reason, cinder has a whole bunch of `gl::Scoped*` structs that automatically save and restore the affected state using RAII to try and minimize this issue. Here’s the code i’ve changed to make it work, along with a few short annotations to help you along. Hope this makes sense.

```cpp
void DrawingCanvasApp::update()
{
    if ( mMousePressed ) // Don't bother binding the fbo if we're not going to draw anything
    {
        gl::ScopedMatrices matrices; // Automatically restore the current matrices when we exit this scope
        gl::setMatricesWindow( mFBO->getSize() );
        gl::ScopedViewport vp( ivec2 ( 0 ), mFBO->getSize() ); // Automatically restore the current viewport when we exit this scope
        gl::ScopedFramebuffer buffer { mFBO }; // Automatically unbind the framebuffer when we exit this scope.
        gl::ScopedBlendAlpha blend;
        
        {
            gl::ScopedColor color ( ColorA( 1, 0, 0, 1 ) ); // Automatically restore the color when we exit this scope. Seeing a pattern here? ;)
            gl::drawSolidCircle( mMouse, 25.0f, 32 );
        }
    }
    
}

void DrawingCanvasApp::draw()
{
    // There's a few places in here where some gl::Scoped* calls would be good hygiene, but i'll leave that up to you.
    gl::clear( Color( 0.0, 0.0, 1.0 ) );
    gl::enableAlphaBlending();
    gl::setMatricesWindow( getWindowSize() );
    gl::viewport( getWindowSize() );
    
    gl::draw( mFBO->getColorTexture() );
}

void DrawingCanvasApp::resetFbo()
{
    gl::setMatricesWindow( mFBO->getSize() );
    gl::viewport( mFBO->getSize() );
    gl::ScopedFramebuffer buffer { mFBO };
    gl::clear ( ColorA( 0, 1, 0, 1 ) );
}

```

---

<div class="post-metadata">

**Author:** ![Renderman](https://avatars.discourse-cdn.com/v4/letter/r/dbc845/32.png) [@Renderman](https://discourse.libcinder.org/u/Renderman)\
**Post date:** [November 12, 2019, 12:18pm UTC](https://discourse.libcinder.org/t/fbo-drawing-app/1585/3 "2019-11-12T12:18:33Z")

</div>

Thanks for your help, lithium! This solves my problem and is really informative.

---

<div class="post-metadata">

**Author:** ![Renderman](https://avatars.discourse-cdn.com/v4/letter/r/dbc845/32.png) [@Renderman](https://discourse.libcinder.org/u/Renderman)\
**Post date:** [November 14, 2019, 4:52pm UTC](https://discourse.libcinder.org/t/fbo-drawing-app/1585/4 "2019-11-14T16:52:26Z")

</div>

Hi @lithium, back again!  
I’m trying to add an elaboration to my code - using nanovg to render…  
With your corrections, this code seems to work properly with the standard cinder calls, but not with the nanovg versions shown below.  
As I understand it, “nvgBeginFrame() defines the size of the window to render to in relation currently set viewport (i.e. glViewport on GL backends)”  
But for whatever reason, I have some sort of viewport issue.  
Any thoughts? Thanks again!

```
class Painting {
public:
    
    gl::FboRef mFbo;
    ci::vec2 mDim;
    
    void setup(const ci::vec2& dim)
    {
        mDim = dim;
        mFbo = ci::gl::Fbo::create( mDim.x, mDim.y, ci::gl::Fbo::Format().colorTexture() );
    }
    
    void render(nvg::ContextGL& vg)
    {
        gl::ScopedMatrices matrices;
        gl::setMatricesWindow( mFbo->getSize() );
        gl::ScopedViewport vp( ivec2 ( 0 ), mFbo->getSize() );
        gl::ScopedFramebuffer buffer { mFbo };
        gl::ScopedBlendAlpha blend;
        
        ci::gl::clear( ColorA( 0, randFloat(), 0, 1 ) );
        ci::gl::clear( GL_STENCIL_BUFFER_BIT );
        
        vg.beginFrame( mDim, getWindow()->getContentScale() );
        vg.translate( ci::vec2( 0.0f, 0.0f ) );
        vg.fillColor( ColorA( ci::randFloat(),0,0,1 ) );
        vg.circle( mDim.x * 0.5f, mDim.y * 0.5f, 50.0f );
        vg.fill();
        vg.endFrame();
        
        /*
        {
            gl::ScopedColor color ( ColorA( randFloat(), 0, 0, 1 ) );
            gl::drawSolidCircle( mDim * 0.5f, 50.0f, 32 );
        }
        */
    }
};

class TestApp : public App {
  public:
	void setup() override;
	void draw() override;
    
    shared_ptr<ci::nvg::ContextGL> mCtx;
    std::shared_ptr<Painting> mPainting;
};

void TestApp::setup()
{
    mCtx = make_shared<ci::nvg::ContextGL>(nvg::createContextGL());
    
    mPainting = std::make_shared<Painting>();
    mPainting->setup( ci::vec2( 200, 200 ) );
}

void TestApp::draw()
{
    if( ! mPainting )
        return;
    
    mPainting->render( *mCtx );
    
    gl::clear( Color( 1, 0, 1 ) );
    ci::gl::clear( GL_STENCIL_BUFFER_BIT );
    
    gl::enableAlphaBlending();
    
    {
        gl::ScopedMatrices matrices;
        gl::setMatricesWindow( getWindowSize() );
        gl::ScopedViewport vp( ivec2 ( 0 ), getWindowSize() );
        
        gl::draw( mPainting->mFbo->getColorTexture() );
        
        /*
        {
            gl::ScopedColor color ( ColorA( 0, 0, 1, 1 ) );
            gl::drawSolidCircle( ci::vec2( getWindowSize() ) * 0.5f, 100.0f, 32 );
        }
        */

        auto& vg = *mCtx;

        vg.beginFrame( ci::vec2( getWindowSize() ), getWindow()->getContentScale() );
        vg.translate( ci::vec2( 0.0f, 0.0f ) );
        vg.fillColor( ColorA( 0,0,1,1 ) );
        vg.circle( getWindowWidth() * 0.5f, getWindowHeight() * 0.5f, 100.0f );
        vg.fill();
        vg.endFrame();
    }
}
```

---

<div class="post-metadata">

**Author:** ![lithium](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/lithium/32/38_2.png) [@lithium](https://discourse.libcinder.org/u/lithium)\
**Post date:** [November 15, 2019, 12:34am UTC](https://discourse.libcinder.org/t/fbo-drawing-app/1585/5 "2019-11-15T00:34:01Z")

</div>

What’s it supposed to look like? I’m getting this (with the green bit flashing like crazy), which seems in line with what you’ve written

 ![Untitled%202](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/fe7c9066f6e6b24295e7ec7d75118d837e1d93bc.png)

---

<div class="post-metadata">

**Author:** ![Renderman](https://avatars.discourse-cdn.com/v4/letter/r/dbc845/32.png) [@Renderman](https://discourse.libcinder.org/u/Renderman)\
**Post date:** [November 15, 2019, 12:47am UTC](https://discourse.libcinder.org/t/fbo-drawing-app/1585/6 "2019-11-15T00:47:47Z")

</div>

The strobing is a product of the randFloat() in the colors just for testing, sorry about that.

Here’s what it should look like…

 ![Output](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/8ecf6349bc3213abfb7702e9a7c960c43f830d28.png)  
Thanks for your help!

---

<div class="post-metadata">

**Author:** ![lithium](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/lithium/32/38_2.png) [@lithium](https://discourse.libcinder.org/u/lithium)\
**Post date:** [November 15, 2019, 1:40am UTC](https://discourse.libcinder.org/t/fbo-drawing-app/1585/7 "2019-11-15T01:40:33Z")

</div>

This seems like a problem with nanovg itself. i know it pokes at a bit of gl state (which is mentioned in the docs) but i don’t think it cleans up perfectly. For example it leaves a different vao bound, which confuses cinder’s internal state (call `gl::context()->sanityCheck()` to see what the problems are)

It’s likely not a viewport issue at all, but an errant depth mode, stencil state, or something like that. Fire up your favourite OpenGL debugger and have a look at the GL state before and after nanovg gets involved and hopefully something will jump out at you.

---

<div class="post-metadata">

**Author:** ![Renderman](https://avatars.discourse-cdn.com/v4/letter/r/dbc845/32.png) [@Renderman](https://discourse.libcinder.org/u/Renderman)\
**Post date:** [November 18, 2019, 1:45am UTC](https://discourse.libcinder.org/t/fbo-drawing-app/1585/8 "2019-11-18T01:45:21Z")

</div>

Hmm. Will poke around and see what I find. Thanks for your help!
