libgdxframebufferscene2d

Using frame buffers with Scene2D


I've been having some trouble with a custom Scene2D actor whose draw method uses a FrameBuffer. The original idea was to draw to the FrameBuffer with one shader and then draw the contents of the FrameBuffer to the screen with another, but that will have to wait. The problem I have right now is to get the contents of the FrameBuffer to properly fill the space I expected the Actor to take up on the screen. After much experimentation, I have arrived at something that appears to work, but I still don't understand why it works.

Here's the draw method:

@Override
public void draw(Batch batch, float parentAlpha) {
    
    batch.end();
    buffer.begin();
    ScreenUtils.clear(1,0,0,1);
    batch.begin();
    batch.draw(texture,0,0,640,640); // This is the window size!
    batch.end();
    buffer.end();
    batch.begin();
    batch.draw(buffer.getColorBufferTexture(),
               getX(),
               getY(),
               getWidth(),
               getHeight(),
               0,
               0,
               buffer.getColorBufferTexture().getWidth(),
               buffer.getColorBufferTexture().getHeight(),
               false,
               true);

}

I understand that the FrameBuffer uses pixel coordinates rather than the "world" coordinates that Scene2D uses for Actor's position and size. What I don't understand is why, if render a texture to the FrameBuffer at anything less than the actual window size, the color buffer texture from the FrameBuffer gets rendered at less than the Actor's size on the screen.

I guess what I'm looking for are answers to the following questions.

(1) What size should the FrameBuffer be?

My initial thought was that it should be the size of the texture I intend to draw to it.

(2) When I draw to the FrameBuffer should I be using a different projection matrix?

My best guess for a proper solution is that when I'm drawing to the FrameBuffer I should change the SpriteBatch's projection matrix to account for the fact that the FrameBuffer isn't the same size as the application window. Unfortunately, I haven't been able to find a useful example or tutorial online.


Solution

  • Since you're using the same SpriteBatch as the Stage, you need to swap its projection matrix to do this.

    Also, if your Stage uses a Viewport that letterboxes the scene, such as FitViewport or an ExtendViewport that has some maximum ratio set on it, then you will also have to swap the viewport, but I will ignore that for now.

    If you are drawing a texture to the frame buffer that fills the entire frame buffer dimensions, that simplifies things. Then you can use an identity matrix and draw the texture to fit the 2x2 square around the world origin. This is the defined view of identity projection (not just magic numbers). It will be stretched appropriately.

    My libGDX is rusty, but it should look something like this:

    private final Matrix4 tempStageMatrixCopy = new Matrix4();
    
    @Override
    public void draw(Batch batch, float parentAlpha) {
        
        batch.end();
        tempStageMatrixCopy.set(batch.getProjectionMatrix()); // store stage's matrix value
    
        buffer.begin();
        ScreenUtils.clear(1,0,0,1);
    
        batch.getProjectionMatrix().idt(); // set the batch's projection to identity
        batch.begin();
        batch.draw(texture, -1f, 1f, 2f, -2f); // If this is vertically flipped from what you want
                                               // use -1f, -1f, 2f, 2f
        batch.end();
        buffer.end();
    
        batch.setProjectionMatrix(tempStageMatrixCopy); // restore stage's matrix
        batch.begin();
        batch.draw(buffer.getColorBufferTexture(),
                   getX(),
                   getY(),
                   getWidth(),
                   getHeight(),
                   0,
                   0,
                   buffer.getColorBufferTexture().getWidth(),
                   buffer.getColorBufferTexture().getHeight(),
                   false,
                   true);
    
    }
    

    As mentioned above, this is assuming whatever texture is, it has the aspect ratio to match the FrameBuffer, and both the texture and frame buffer have dimensions that are appropriate for the texture to stretch to fill the buffer. If you are doing something more complicated, you probably need to create a Camera to use with just this FrameBuffer, and then draw things relative to the camera and use the camera's matrix in the sprite batch.