How to get ofFbo data without drawing to screen

#1

I want to blur an image using shader, and then save the resulting image. I started from an example of openframework v0.10.1, that is, shader/09_gaussianBlurFilter. I added an ofPixels (called inputPixels) and ofImage (called outputImage) to achieve my objective. Below are my codes, where I added comments for what I added by “added by me.”

The program works. It can save an image file, same as the screen shows. However, I don’t want to draw anything to screen. I wanted to blur an image and save the result in a more clean way. So I tried to remove “fboBlurOnePass.draw(0, 0);” and “fboBlurTwoPass.draw(0, 0);.” However, the program failed to save a meaningful image file if I remove the two.

I was wondering if someone can tell me how to modify the codes, so as to blur an image without drawing anything to screen. Thank you very much.

void ofApp::draw(){
    float blur = ofMap(mouseX, 0, ofGetWidth(), 0, 4, true);
    
    //----------------------------------------------------------
    fboBlurOnePass.begin();
    
    shaderBlurX.begin();
    shaderBlurX.setUniform1f("blurAmnt", blur);

    image.draw(0, 0);
    shaderBlurX.end();
    fboBlurOnePass.end();
    
    //----------------------------------------------------------
    fboBlurTwoPass.begin();
    
    shaderBlurY.begin();
    shaderBlurY.setUniform1f("blurAmnt", blur);
    
    fboBlurOnePass.draw(0, 0);
    fboBlurOnePass.readToPixels(inputPixels); // added by me
    shaderBlurY.end();
    fboBlurTwoPass.end();
    
    //----------------------------------------------------------
    ofSetColor(ofColor::white);
    fboBlurTwoPass.draw(0, 0);
    fboBlurTwoPass.readToPixels(inputPixels); // added by me

    outputImage.setFromPixels(inputPixels); // added by me
    outputImage.saveImage("screenshot.png"); // added by me
}
#2

Hello

I have not tested the code but, you should not readToPixels in the middle of fboBlurTwoPass

 fboBlurTwoPass.begin();
    
    shaderBlurY.begin();
    shaderBlurY.setUniform1f("blurAmnt", blur);
    
    fboBlurOnePass.draw(0, 0);
    //fboBlurOnePass.readToPixels(inputPixels); // added by me
    shaderBlurY.end();
    fboBlurTwoPass.end();

To do not draw comment

//fboBlurTwoPass.draw(0, 0);

Later on keypress for example add, if you that in draw the image is overwriten on every cycle and thats not good

    fboBlurTwoPass.readToPixels(inputPixels); // added by me

    outputImage.setFromPixels(inputPixels); // added by me
    outputImage.saveImage("screenshot.png"); // added by me
#3

Thanks for your suggestions. I moved the codes about “readToPixels” and codes about saving files, to keyPressed(), as follows. But, still I cannot remove fboBlurOnePass.draw(0, 0); and fboBlurTwoPass.draw(0, 0);. That is, I can save the content of fbo only after I do drawing. It’s so weird. It means that draw() is not just drawing…

    image.draw(0, 0);
    shaderBlurX.end();
    fboBlurOnePass.end();
    
    //----------------------------------------------------------
    fboBlurTwoPass.begin();
    
    shaderBlurY.begin();
    shaderBlurY.setUniform1f("blurAmnt", blur);
    
    **fboBlurOnePass.draw(0, 0);**
    shaderBlurY.end();
    fboBlurTwoPass.end();
    
    //----------------------------------------------------------
    ofSetColor(ofColor::white);
    **fboBlurTwoPass.draw(0, 0);**
}

void ofApp::keyPressed(int key){
    fboBlurOnePass.readToPixels(inputPixels); // added by me
    fboBlurTwoPass.readToPixels(inputPixels); // added by me

    outputImage.setFromPixels(inputPixels); // added by me
    outputImage.saveImage("screenshot.png"); // added by me
}
#5

You can move the code that you have in draw method to update method

You do not need to draw fboBlurTwoPass to get the effect but you need to draw boBlurOnePass because you are drawing this inside fboBlurTwoPass.

Also do not need this line, you only need the pixels from fboBlurTwoPass

//fboBlurOnePass.readToPixels(inputPixels); // added by me
#6

When used between shader.begin() and shader.end(), the draw() method will call your shader with the texture of your Fbo (or image) as the texture input of your shader.
That is why you need to have fboBlurOnePass.draw() between shader.begin() and shader.end().

The fboBlurTwoPass may be considered as a virtual screen. With fboBlurTwoPass.begin(), you’re just telling the system that you want to work in this virtual screen.

As pandereto said, it’s not need to draw the fboBlurTwoPass as it already has your blurred image inside.

#7

Thanks a lot for pandereto’s help and oxillo’s help. I understand the principle now. Thanks a lot.