Libgdx 嵌套帧缓冲区

4
我将在LevelScreen渲染方法中使用FBO来呈现多通道模糊着色器。我想要实现的是,在MenuScreen上应用另一种模糊效果,使其在背景上呈现LevelScreen的效果。
以下是伪代码:
    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();
    }

问题在于levelScreen.render()函数已经包含了fbo.begin()fbo.end(),场景直接渲染在屏幕上。

有没有一种方法可以正确处理嵌套的fbo?

2个回答

8
你可以手动处理事情,使用自定义类来处理帧缓冲区,可能由Stack支持,使用startRender(FrameBuffer)endRender()等方法进行适当的管理,例如关闭旧的FrameBuffer并打开新的FrameBuffer等。
此外,请尽量避免在渲染方法中创建新对象。
Sprite background = new Sprite(fbo.getColorBufferTexture());

如果这是一个字段,它应该可以正常工作。

编辑:

如果我理解正确,您当前的问题看起来像这样:

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

但是你不能同时打开多个帧缓冲,对吧?因此,创建一个基于堆栈的类来管理帧缓冲,简单的东西就可以:

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

然后您可以像这样使用它:
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();

然而我没有尝试过使用或不使用管理器,但它应该能解决你的问题。我不确定每次更改FrameBuffer时是否需要重新打开渲染器和批处理(我通常这样做)。


你能再解释一下关于frameBuffer堆栈的想法吗?我不太清楚如何实现。 - Alexandre GUIDET
我刚刚尝试了你的解决方案,它运行良好且透明,非常感谢你提供这个绝妙的提示! - Alexandre GUIDET
@kajacx 谢谢兄弟,完美运作。代码非常简洁,我原以为解决这个问题会很麻烦。 - Gintas_
这个方案对于我来说非常好,但是如果你有复杂的渲染策略,会在使用帧缓冲和不使用它们之间进行切换时要小心(例如,在进入暂停菜单时开始使用它),可能会发生在渲染循环内部更改渲染策略,并最终调用空堆栈上的stack.pop()的情况。 - Azurlake
@azurlake 是的,最好的方法是使用try-with-resources。 - kajacx

1

这是一个简单的解决方案,但也许这就是你所需要的:

将你的LevelScreen分解为不同的方法:

protected void render(float delta){
    update(delta);
    drawFBOs(delta);
    drawMain(delta);
}

实现上述三种方法。然后还要创建这个方法,MenuScreen可以调用它并传入自己的FBO引用。
protected void renderToFBO(float delta, FrameBuffer target){
    update(delta); //if necessary
    drawFBOs(delta);
    target.begin();
    drawMain(delta);
    target.end();
}

如果需要设置某些参数,您可以在drawMain方法构造函数中添加一个布尔值来确定它是直接绘制到屏幕还是帧缓冲区。

我已经尝试过这个解决方案,它运行良好,但似乎不像kajacx提出的那个解决方案那样可重用。感谢您的建议,两种解决方案都有效。 - Alexandre GUIDET

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接