如何正确使用 LIBGDX FrameBuffer

Posted

技术标签:

【中文标题】如何正确使用 LIBGDX FrameBuffer【英文标题】:How to use LIBGDX FrameBuffer correctly 【发布时间】:2020-06-11 13:11:45 【问题描述】:

这是我的第一个问题。我试图查看源代码(link) 搜索了***并阅读了一些一般解释。但是对于如何正确设置(或理解)它,我没有任何智慧。

我不懂投影矩阵或大部分 GL 术语。 我不知道当您应该更新相机时设置相机 (camera.setToOrtho()) 会发生什么 或何时设置批次的投影矩阵。到目前为止,我对它的唯一使用仅限于下面所示的 draw() 方法中使用的代码。

我有一个基于图块的分层游戏。我想使用 FrameBuffer 将水层绘制到 TextureRegion。所以我以后可以尝试用着色器来操作它。我认为这就是这样做的方法。

(我没有使用任何“Scene2d”或“Tiled”类)

Example of what the layer looks like

我如何绘制图层:

public void draw(OrthographicCamera camera) 

        Gdx.gl.glClearColor(0, 0, 0, 0);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA,GL20.GL_ONE_MINUS_SRC_ALPHA); // [1]
        BATCH.setProjectionMatrix(camera.combined);
        BATCH.begin();

        for (int i = 0; i < NUM_LAYERS; i++) 
            if (RENDER[i]) 
                if (SORT[i]) Collections.sort(LAYERS.get(i));
                for (DrwDat dat: LAYERS.get(i))                            // [2]
                    dat.draw(BATCH);
                
            
        
        BATCH.end();
    

1。不知道这是做什么的,但我似乎不需要它。 [2]。 DrwDat 只是一个保存可绘制对象的类。在这种情况下,这些是图块。

好的。因此,在绘制循环中,我放置了一个在绘制水层时要调用的方法。目前 我想绘制应该在屏幕上显示的每个图块,而不是将它们绘制到缓冲区。然后:将缓冲区绘制到屏幕上。

问题:我可以在下面的“renderFBO()”方法中添加什么使其工作?为什么?基本上在不使用 FrameBuffer 的情况下让屏幕准确显示它会显示的内容

(当前的代码纯粹是绝望,因为我试图使用 youtube 教程作为模板而不理解它。)

private void renderFBO(OrthographicCamera camera, int layer) 
        BATCH.end();
        frameBuffer.begin();
        Gdx.gl.glClearColor(0, 0, 0, 0);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);


        BATCH.begin();

        camera.setToOrtho(false);
        camera.position.set(FocusPoint.position.x,FocusPoint.position.y,0);  // [3]
        camera.update();
        BATCH.setProjectionMatrix(camera.combined);

        for (DrwDat dat: LAYERS.get(layer)) 
            dat.draw(BATCH);
        

        frameBuffer.end();                                                             // [4]
        BATCH.end();
        camera.setToOrtho(false);

        //BATCH.setProjectionMatrix(camera.combined);
        BATCH.begin();
        BATCH.draw(bufferTexture.getTexture(),0,0,Settings.SCREEN_W,Settings.SCREEN_H);
        BATCH.end();

        camera.position.set(FocusPoint.position.x,FocusPoint.position.y,0);
        camera.update();
        BATCH.setProjectionMatrix(camera.combined);

        BATCH.begin();


[3]。 FocusPoint 只是相机跟随的一个点(例如:鼠标点击位置)

[4]。出于某种原因,将 framebuffer.end() 设置在 batch.end() 下面不会渲染任何内容。

这是我初始化 FrameBuffer 的方法:

private FrameBuffer frameBuffer = new FrameBuffer(Pixmap.Format.RGBA8888,Settings.SCREEN_W,Settings.SCREEN_H,false);
private TextureRegion bufferTexture = new TextureRegion(frameBuffer.getColorBufferTexture());

如果这太笼统了,我很抱歉。但任何帮助表示赞赏。我能读到的任何提示也会 很有帮助。

【问题讨论】:

【参考方案1】:

关于您的第 1 点:您不需要该行,因为 SpriteBatch 自己管理混合并将覆盖您在调用 batch.begin() 之前设置的任何混合状态。

关于你的第 4 点:像这样把它们弄乱,你根本就没有画到 FBO。可能是您的 FBO 设置或绘制方式有问题,因此无法正常工作,但像往常一样直接在屏幕上绘制会使它看起来可以正常工作。

就像在您的示例中一样,这将假设它正在中断在batch.begin()batch.end() 之间完成的现有绘图。所以它会在你的 begin()end() 调用之间被调用,如下所示:

    public void draw(OrthographicCamera camera) 
        Gdx.gl.glClearColor(0, 0, 0, 0);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        BATCH.setProjectionMatrix(camera.combined);
        BATCH.begin();

        renderFbo();

        BATCH.end();
    

无需使用相机来绘制帧缓冲区内容以覆盖整个屏幕。您可以使用单位投影矩阵以及 OpenGL 将默认投影定义为 2 个单位宽和高,以 (0, 0) 为中心的知识。

您无需结束和开始批处理。在切换渲染表面之前刷新它会更简单一些。

private final Matrix4 originalMatrixTemp = Matrix4();
private static final Matrix4 IDENTITY = Matrix4();

public void renderFbo() 

    // Finish drawing anything queued before we switch the frame buffer.
    batch.flush(); 

    // Store the current Batch state for things we're going to temporarily change.
    originalMatrixTemp.set(BATCH.getProjectionMatrix());
    ShaderProgram originalShader = BATCH.getShaderProgram();
    int originalBlendSrcFunc = BATCH.getBlendSrcFunc();
    int originalBlendDstFunc = BATCH.getBlendDstFunc();

    // No need to set up camera projection again. 
    // It was already set before calling this method.

    // This ensures alpha is preserved in case you have overlapping translucent sprites.
    BATCH.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE);

    // Draw to FBO
    frameBuffer.begin();
    Gdx.gl.glClearColor(0, 0, 0, 0);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    for (DrwDat dat: LAYERS.get(layer)) 
        dat.draw(BATCH);
    
    BATCH.flush();
    frameBuffer.end();

    /* When you have a custom shader for rendering FBO contents, here's where 
       you would prepare it.
    BATCH.setShader(waterFboShader);
    waterFboShader.setUniformf("u_someUniformAttribute", someUniformAttribute);
    etc.
    */

    // Ensure we're drawing the frame buffer texture without modifying its color.
    BATCH.setColor(Color.WHITE);

    // Dimensions to the corners of the screen when using an identity matrix for 
    // projection. Negative height to flip vertically. (OpenGL image space and world 
    // space have opposite Y directions for some reason.)
    BATCH.setProjectionMatrix(IDENTITY);

    BATCH.draw(frameBuffer.getColorBufferTexture(), -1, 1, 2, -2);
    BATCH.flush();

    // Restore state from before this method was called.
    BATCH.setShader(originalShader);
    BATCH.setProjectionMatrix(originalMatrixTemp);
    BATCH.setBlendFunction(originalBlendSrcFunc, originalBlendDstFunc);

在存储原始矩阵时,您必须将其值复制到您自己的临时实例中,因为 SpriteBatch 会将其自己的最终实例传递给您,当您应用单位矩阵时,其值即将被覆盖。

【讨论】:

评论不用于扩展讨论;这个对话是moved to chat。 我用一个更简单的程序尝试了你的例子。它奏效了。所以我关于在 Libgdx 中正确使用帧缓冲区的问题已经得到解答。即使它对我的项目不起作用。还有一些我做错了。

以上是关于如何正确使用 LIBGDX FrameBuffer的主要内容,如果未能解决你的问题,请参考以下文章

Libgdx/Java ShadowMap 和 Framebuffer 使用相机更新

LibGDX - 无法从 FrameBuffer 获取纹理

libgdx 中的 FrameBuffer 大小

libGDX:FrameBuffer 大小限制 == 最大纹理大小?

LibGDX FrameBuffer 混合

LibGDX - 绘制到 FrameBuffer 不起作用