Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

libgdx nested FrameBuffer

I use FBO to render multi pass blur shader in my LevelScreen render method. What I want to achieve is to make a MenuScreen that render the LevelScreen on background applying another blur effect on top of it.

here is the pseudo code

    protected void render(float delta) {
        // render the scene to the FBO
        fbo.begin();
        levelScreen.render(delta);
        fbo.end();

        // retrieve texture and flip Y
        Sprite background = new Sprite(fbo.getColorBufferTexture());
        background.flip(false, true);

        // apply the blurX shader and
        // render the sprite
        batch.setShader(blurH);
        batch.begin();
        background.draw(batch);
        batch.end();
    }

The problem is that the levelScreen.render() function already contains a fbo.begin() fbo.end() and the scene is directly rendered on the screen.

Is there a way to handle properly nested fbo?

like image 792
Alexandre GUIDET Avatar asked Feb 13 '23 04:02

Alexandre GUIDET


1 Answers

You can handle things manually, use custom class to handle the FrameBuffers, possibly backed by Stack, with methods like startRender(FrameBuffer) and endRender() whch will properly manage things, like close old FrameBuffer and open new one then, etc.

Also, try to avoid creating new objects inside the render methods.

Sprite background = new Sprite(fbo.getColorBufferTexture());

This should work just fine if this was a field instead.

EDIT:

If I understand you correctly, your current problem looks like this:

FrameBuffer fb1 = ..., fb2 = ...;
fb1.begin();
// draw something on fb1
fb2.begin();
// draw something on fb2
fb2.end();
// draw something on fb1 again
fb1.end();

But you can't have multiple FrameBuffers opened at the same time, right? So create a stack-based class to manage the FrameBuffers for you, something simple can do:

class FrameBufferManager {
    private Stack<FrameBuffer> stack = new Stack<FrameBuffer>();

    public void begin(FrameBuffer buffer) {
        if (!stack.isEmpty()) {
            stack.peek().end();
        }
        stack.push(buffer).begin();
    }

    public void end() {
        stack.pop().end();
        if (!stack.isEmpty()) {
            stack.peek().begin();
        }
    }
}

Then you use it like this:

FrameBuffer fb1 = ..., fb2 = ...;
FrameBufferManager manager = ...;

manager.begin(fb1);
// draw something on fb1
manager.begin(fb2);
// draw something on fb2
manager.end();
// draw something on fb1 again
manager.end();

However i haven't tried this with or without the manager, but it should solve you problem. I'm not sure if you will need to re-open renderers and batches every time you change FrameBuffer (I usually do).

like image 153
kajacx Avatar answered Mar 15 '23 00:03

kajacx