如何正确使用LIBGDX FrameBuffer [英] How to use LIBGDX FrameBuffer correctly

查看:134
本文介绍了如何正确使用LIBGDX FrameBuffer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的第一个问题.我尝试查看源代码(链接) 搜索stackoverflow并阅读一些常规说明.但是我对如何正确设置(或理解)它没有任何明智的认识.

我不了解投影矩阵或许多GL术语. 我不知道在设置相机(camera.setToOrtho())时应更新相机时会发生什么 或何时设置批次的投影矩阵.到目前为止,我对此的唯一使用仅限于下面所示的draw()方法中使用的代码.

我有一个带有图层的基于图块的游戏.我想使用FrameBuffer将水层绘制到TextureRegion.因此,我以后可以尝试使用着色器对其进行操作.我认为这就是做到这一点的方法.

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

图层外观示例

我如何绘制图层:

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].由于某些原因,在batch.end()下面有framebuffer.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:您不需要该行,因为SpriteBatch自行管理混合,并且将覆盖您在调用batch.begin()之前设置的任何混合状态. /p>

关于第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将为您传递其自己的最终实例,该最终实例在应用身份矩阵时将被覆盖.

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.

解决方案

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.

这篇关于如何正确使用LIBGDX FrameBuffer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆