ofFbo does not work in the second window


#1

I tried to get ofFbo work in the second GLFW window in the multiWindowOneAppExample , but failed. I know multiple ofFbo will work using the multi-windows & multi-apps, but I am not sure if it is possible to use one or multiple ofFbos in the multi-windows & single-app scenarios, especially in the second or third window. Please help me to work this out.

I understand ofFbo has related to the renderer, so I explicitly set the ofFbo’s renderer by passing the ofFbo::Settings sturcture. But it turns out wont work.

My working environment is Linux16.04_x64 + OF0.9.8 stable.

Here are my codes:

// main.cpp
#include "ofMain.h"
#include "ofApp.h"
#include "ofAppGLFWWindow.h"

//========================================================================
int main( ){

    ofGLFWWindowSettings settings;
    settings.width = 300;
    settings.height = 300;
    settings.setPosition(ofVec2f(0,0));
    settings.resizable = true;
    shared_ptr<ofAppBaseWindow> mainWindow = ofCreateWindow(settings);
   
    settings.width = 300;
    settings.height = 300;
    settings.setPosition(ofVec2f(300,0));
    settings.resizable = true;
    settings.shareContextWith = mainWindow;
    shared_ptr<ofAppBaseWindow> secondWindow = ofCreateWindow(settings);

    shared_ptr<ofApp> mainApp(new ofApp);

    mainApp->renderer1= dynamic_pointer_cast<ofBaseGLRenderer>(mainWindow->renderer());
    mainApp->renderer2= dynamic_pointer_cast<ofBaseGLRenderer>(secondWindow->renderer());
    
    ofAddListener(secondWindow->events().draw, mainApp.get(), &ofApp::drawSecondWindow, OF_EVENT_ORDER_APP);
    ofAddListener(secondWindow->events().update, mainApp.get(), &ofApp::updateSecondWindow,OF_EVENT_ORDER_APP);

    ofRunApp(mainWindow, mainApp);
    ofRunMainLoop();

}


// ofApp.h
#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{
    public:
	    void setup();
	    void update();
	    void draw();
	
	    void keyPressed(int key);
	    void keyReleased(int key);
	    void mouseMoved(int x, int y);
	    void mouseDragged(int x, int y, int button);
	    void mousePressed(int x, int y, int button);
	    void mouseReleased(int x, int y, int button);
	    void mouseEntered(int x, int y);
	    void mouseExited(int x, int y);
	    void windowResized(int w, int h);
	    void dragEvent(ofDragInfo dragInfo);
	    void gotMessage(ofMessage msg);
	
        void setupSecondWindow();
        void drawSecondWindow(ofEventArgs & args);
        void updateSecondWindow(ofEventArgs & args);

        ofFbo fbo1;
        ofFbo fbo2;		
        std::shared_ptr<ofBaseGLRenderer> renderer1, renderer2;

};

// ofApp.cpp
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup()
{    
    ofFbo::Settings settings1(renderer1);
    ofFbo::Settings settings2(renderer2);
    
    settings1.width = 180;
    settings1.height = 100;
    settings1.internalformat = GL_RGBA;
    settings1.numSamples = ofFbo::maxSamples();

    settings2.width = 180;
    settings2.height = 100;
    settings2.internalformat = GL_RGBA;
    settings2.numSamples = ofFbo::maxSamples();
    
    fbo1.allocate(settings1);
    fbo2.allocate(settings2);

}

//--------------------------------------------------------------
void ofApp::update()
{

    fbo1.begin();
    {
        ofClear(0,0,0);
        ofBackground(ofColor::yellow);
        ofSetColor(ofColor::red);
        ofDrawBitmapString("Frame buffer object1", 10, 20);

        // animate the cube
        ofPushMatrix();
        ofTranslate(90, 60);
        ofRotate(sin(ofGetElapsedTimef()) * 180);
        ofDrawRectangle(-25, -25, 50, 50);
        ofPopMatrix();
    }
    fbo1.end();
  
}

//--------------------------------------------------------------
void ofApp::updateSecondWindow(ofEventArgs & args)
{
    fbo2.begin();
    {
        ofClear(0,0,0);
        ofBackground(ofColor::white);
        ofSetColor(ofColor::green);
        ofDrawBitmapString("Frame buffer object2", 10, 20);

        // animate the cube
        ofPushMatrix();
        ofTranslate(90, 60);
        ofRotate(sin(ofGetElapsedTimef()) * 180);
        ofDrawRectangle(-25, -25, 50, 50);
        ofPopMatrix();
    }
    fbo2.end();
    
}

//--------------------------------------------------------------
void ofApp::drawSecondWindow(ofEventArgs & args)
{

    ofBackground(ofColor::black);
    ofSetColor(ofColor::white);
    ofDrawBitmapString("Second Window", 20, 20);

    // draw the fbo
    ofSetColor(ofColor::white);    
    fbo2.draw(30,30);
}

//--------------------------------------------------------------
void ofApp::draw()
{

    ofBackground(ofColor::black);
    ofSetColor(ofColor::white);
    ofDrawBitmapString("First Window", 20, 20);

    // draw the fbo
    ofSetColor(ofColor::white);
    fbo1.draw(30,30);
}

And here is the snapshot of the running programming:


The fbo2 in the second window is totally black.


#2

if you need complex setups like this is always adviced to use a multi app layout instead of 1 app for all the windows.

Also fbos created in one context won’t work in another even if the context is shared. in that case you could draw the fbo texture in any of the shared contexts but not use th fbo itself


#3

Thanks! @arturo

Also fbos created in one context won’t work in another even if the context is shared.

You are right, and it occurs to me that what if I create the ofFbo’s in their own context? So I do some modification as follows:

void ofApp::setup()
{
    ofFbo::Settings settings1(dynamic_pointer_cast<ofBaseGLRenderer>(mainWindow->renderer()));
    ofFbo::Settings settings2(dynamic_pointer_cast<ofBaseGLRenderer>(secondWindow->renderer()));

    settings1.width = 180;
    settings1.height = 100;
    settings1.internalformat = GL_RGBA;
    settings1.numSamples = ofFbo::maxSamples();

    settings2.width = 180;
    settings2.height = 100;
    settings2.internalformat = GL_RGBA;
    settings2.numSamples = ofFbo::maxSamples();

    mainWindow->makeCurrent();
    fbo1.allocate(settings1);
    secondWindow->makeCurrent();
    fbo2.allocate(settings2);

}

Turns out, this time in the second window, the fbo2 displays something in it.


But happy days do not last long, when I try to draw something complex in the second window’s fbo2, like the squirrel model in the 3DModelLoaderExample, the result is weird.

It seems that depth test is not working.

@arturo is right, maybe I should use the multi-app layout.


#4

Typically I use one application for the rendering with two windows. I render the scene into the fbo in the main window and then just draw the fbo in the second window which is usually a projector or external screen.

Curious about the multi-app setup in a single project. For opencv heavy projects I usually make an OF app for cv tracking, pulling in the camera feed and then send it over via Syphon to another app that has two windows, one for gui and the other for fbo. But those are two separate Xcode projects.
Would there be any benefit to making those two apps in the same xcode project in the main function? Would I be able to transfer the texture from the vision app to the rendering app? Or would that still require syphon?

Thanks!
Nick


#5

Hi, @NickHardeman !
First of all, the multi-app layout here we are talking about is not that kind of ‘multiple applications ( programs)’ . Multi-app layout in OF is about multiple class instances inherited from ofBaseApp, running in a single OF program. You can check into the ‘examples/events/multiWindowExample’ and ‘examples/events/multiWindowOneAppExample’ provided by the OF release.

And, I am curious about how could you achieve this:

I render the scene into the fbo in the main window and then just draw the fbo in the second window which is usually a projector or external screen

Could you post your codes here or give me some hints?


#6

Hi @genleung_lan,

I just use the second window to draw the fbo. So in the update or draw of the main window, I draw into the fbo. Since all of those GL resources are in my ofApp which is part of the mainWindow’s resources.
Something like:

void ofApp::draw() {
     myFbo.begin(); 
         ofClear(255);
        // draw things
    myFbo.end();
}  

void ofApp::drawSecondWindow() {
     ofSetColor(255);
     myFbo.draw();
}

#7

Sorry, late reply.
But I think your code won’t work @NickHardeman
The main window & second window work in different OpenGL context, so any ofFbo bound to the mainwindow could not be correctly drawed in the second window, even the two windows sharing the same context.


#8

Hi @genleung_lan,

Not sure how it works, but I have used this setup for projects over the last couple of years on OSX and Windows.
Something like:
// main.cpp

int main() {
    ofGLFWWindowSettings msettings;
    msettings.setGLVersion(3, 2);
    msettings.title = "Controls";
    auto mainWin    = ofCreateWindow( msettings );
    mainWin->setVerticalSync( false );
    
    ofGLFWWindowSettings rsettings;
    rsettings.numSamples = 0;
    rsettings.setGLVersion(3, 2);
    rsettings.shareContextWith = mainWin;
    rsettings.title = "Render";
    auto renderWin  = ofCreateWindow( rsettings );
    
    auto app = make_shared<ofApp>();
    app->mainWin    = dynamic_pointer_cast<ofAppGLFWWindow>( mainWin );
    app->renderWin  = dynamic_pointer_cast<ofAppGLFWWindow>( renderWin );
    
    ofAddListener( renderWin->events().draw, app.get(), &ofApp::onRenderWindowDraw );

    ofRunApp( mainWin, app );
    ofRunMainLoop();
}

// ofApp.cpp

//--------------------------------------------------------------
void ofApp::setup() {
    ofSetFrameRate( 60 );
    ofRectangle renderRect(0,0,1920,1080);
    mRenderFbo.allocate( renderRect.width, renderRect.height, GL_RGB, 4 );
    mRenderFbo.begin(); { ofClear( 255, 255, 255, 255); } mRenderFbo.end();
}

//--------------------------------------------------------------
void ofApp::update() {
    
}

//--------------------------------------------------------------
void ofApp::draw() {
    // render to the fbo
    mRenderFbo.begin(); {
        ofClear( 10 );
        // draw things into the FBO //
    } mRenderFbo.end();
}

//--------------------------------------------------------------
void ofApp::onRenderWindowDraw( ofEventArgs& args ) {
    ofRectangle srect( 0, 0, renderWin->getWidth(), renderWin->getHeight() );
    ofRectangle frect( 0, 0, mRenderFbo.getWidth(), mRenderFbo.getHeight() );
    ofSetColor( 0 );
    ofDrawRectangle( srect );
    frect.scaleTo( srect, OF_SCALEMODE_FIT );
    ofSetColor( 255 );
    mRenderFbo.draw( frect );
}

#9

Thank you for your time & hints ! @NickHardeman

I tried your code, but the mRenderFbo drawed in the renderWin is totally black. I modified this line

mRenderFbo.allocate( renderRect.width, renderRect.height, GL_RGB, 4 );

to

mRenderFbo.allocate( renderRect.width, renderRect.height, GL_RGB);

then the mRenderFbo could be drawed in the renderWindow, but with poor anti-alias effects. I don’t know is it the OpenGL issues in different OS & hardware platforms.


#10

I have never tried on Linux, based on your tests, seems like my approach won’t work on Linux. :frowning: