【问题标题】:libgdx nested FrameBufferlibgdx 嵌套 FrameBuffer
【发布时间】:2014-10-17 18:23:36
【问题描述】:

我在 LevelScreen 渲染方法中使用 FBO 渲染多通道模糊着色器。我想要实现的是制作一个在背景上呈现 LevelScreen 的 MenuScreen,并在其上应用另一种模糊效果。

这是伪代码

    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?

【问题讨论】:

    标签: libgdx glsl game-engine framebuffer


    【解决方案1】:

    您可以手动处理事情,使用自定义类来处理 FrameBuffers,可能由 Stack 支持,使用像 startRender(FrameBuffer)endRender() 这样的方法可以正确管理事情,比如关闭旧的 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();
    

    但是您不能同时打开多个 FrameBuffer,对吗?因此,创建一个基于堆栈的类来为您管理 FrameBuffers,一些简单的事情就可以做到:

    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 堆栈背后的想法吗?我不知道该怎么做。
    • 我刚刚尝试了您的解决方案,它运行良好且透明,感谢您的精彩提示!
    • @kajacx 谢谢兄弟,完美运行。超级干净,我以为解决它会很麻烦
    • 这个解决方案对我来说也很棒,如果你有复杂的渲染策略从使用帧缓冲区切换到不使用它们(例如,在进入暂停菜单时开始使用它),请小心 -渲染策略可能会在渲染循环中发生变化,最终您会在空堆栈上调用 stack.pop()。
    • @azurlake 是的,最好的方法是使用 try-with-resources。
    【解决方案2】:

    一个简单的解决方案,但这可能就是您所需要的:

    将您的 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 建议的那样可重复使用。感谢您的建议,两种解决方案都有效
    猜你喜欢
    • 2012-12-14
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多