Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use LIBGDX FrameBuffer correctly

This is my first question. I have tried to look at the source (link) Searched stackoverflow and read some general explanations. But i don't get any wiser as to how to set it up (or understand) it correctly.

I don't understand projection-matrices or much of the GL terms. I don't know what happens when you set camera (camera.setToOrtho()) when you should update the camera or when to set the batch's projection matrix. Up to now my only use of this was limited to the code used in the draw() method shown below.

I have a tile-based game with layers. i would like to use a FrameBuffer to draw the water-layer to a TextureRegion. So i can later try to manipulate it with shaders. I Think that would be the way to do this.

(I am not using any "Scene2d" or "Tiled" classes)

Example of what the layer looks like

How i draw my layers:

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. Not sure what this does but i don't seem to need it. [2]. DrwDat is just a class that holds the drawable objects. In this case these are the tiles.

Ok. So in the draw loop i place a method to be called when i draw the water-layer. For now i would like to draw every tile that's supposed to be shown on screen and instead draw them to the buffer. Then: draw the buffer to the screen.

Question: What can i put in the "renderFBO()" method below to make it work? And why? To basically have the screen show exactly what it would show without using the FrameBuffer

(The current code is pure desperation and is the way it is because i tried to use a youtube tutorial as a template without understanding it.)

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 is just a point the camera follows (for instance: mouse click position)

[4]. For some reason having framebuffer.end() below batch.end() renders nothing.

Here is how i initialize the FrameBuffer:

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

I'm sorry if this was too general. But any help is appreciated. Any tips for what i could read would also be very helpful.

like image 545
Dahl Avatar asked Jan 29 '26 20:01

Dahl


1 Answers

Regarding your point 1: You don't need that line because SpriteBatch manages blending for itself and will overwrite whatever blend state you set before calling batch.begin().

Regarding your point 4: By putting them out of order like this, you are not drawing to the FBO at all. Probably there was something set up wrong with your FBO or the way you draw it, so it wasn't working, but drawing directly to the screen like normal makes it look like it's working.

Like in your example, this will work with the assumption that it is interrupting existing drawing done between batch.begin() and batch.end(). So it would get called in between your begin() and end() calls like this:

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

There's no need to mess around with cameras to draw the frame buffer contents to cover the whole screen. You can use an identity projection matrix and the knowledge that OpenGL defines the default projection as 2 units wide and tall, centered at (0, 0).

You don't need to end and begin the batch. It's a little simpler just to flush it before switching render surfaces.

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

When storing the original matrix, you have to copy its value into your own temporary instance because SpriteBatch passes you its own final instance whose values are about to get overwritten when you apply the identity matrix.

like image 89
Tenfour04 Avatar answered Jan 31 '26 09:01

Tenfour04



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!