libgdx: How do i get the result of an orthographic camera through another orthographic camera?

Viewed 105

I would like to have a TV screen on my stage. I have a master scene containing the TV screen and some other things (some sprites) and the scene corresponding to what I want to render in the TV.

So I would like to render the TV scene through an OrthographicCamera and then put the result in something like a sprite which can then be rendered through the master orthographic camera, how to do that?

I know that one can use multiple camera in multiple viewports but these are directly rendered on the pixels of my computer screen.

Any advice would be much appreciated

1 Answers

This can be achieved by rendering the scene from the perspective of a screen camera to a FrameBuffer and then taking the TextureRegion from the FrameBuffer and rendering that to screen and then render the scene again.

For example:

enter image description here

In the above example the cyan rect shows what the in-scene camera sees, and this is rendered to the grey box, it is possible to move both the scene camera and the main camera independentely.

By creating and grabbing the TextureRegion from a FrameBuffer

screenFrameBuffer = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),true);
screenTexture = new TextureRegion(screenFrameBuffer.getColorBufferTexture());
screenTexture.flip(false, true);

all render calls can be made to affect only the screenFrameBuffer by calling screenFrameBuffer.begin(), after screenFrameBuffer.end() is called the next draw call will again affect the actual screen/window.

So in in the render method you can first draw your scene to the FrameBuffer

screenFrameBuffer.begin();
renderScene(screenCamera, Color.DARK_GRAY);
screenFrameBuffer.end();

And then draw it again, followed by the "screen" as a sprite:

renderScene(sceneCamera, Color.BLACK);

batch.setProjectionMatrix(sceneCamera.combined);
batch.begin();
batch.draw(screenTexture, -sceneCamera.viewportWidth / 2.0f,-sceneCamera.viewportHeight / 2.0f,0,0,screenCamera.viewportWidth,screenCamera.viewportHeight,1.0f, 1.0f, 0.0f);
batch.end();

Complete source code for the gif above is:

package com.bornander.sandbox;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.RandomXS128;
import com.badlogic.gdx.math.Vector2;

public class MyGdxGame implements ApplicationListener {

    public static class Ball {
        public Vector2 position = new Vector2();
        public Vector2 velocity = new Vector2();
        public float size = 1.0f;
        public Color color = new Color();

        public void render(ShapeRenderer shapeRenderer) {
            shapeRenderer.setColor(color);
            shapeRenderer.circle(position.x, position.y, size, 16);
        }

        public void update() {
            position.x += velocity.x * Gdx.graphics.getDeltaTime();
            position.y += velocity.y * Gdx.graphics.getDeltaTime();
        }
    }

    static RandomXS128 rnd = new RandomXS128();

    OrthographicCamera sceneCamera;
    OrthographicCamera screenCamera;
    ShapeRenderer shapeRenderer;
    SpriteBatch batch;

    FrameBuffer screenFrameBuffer;
    TextureRegion screenTexture;
    Ball[] balls;

    private static float rnd(float min, float max) {
        return min + rnd.nextFloat() * (max - min);
    }

    @Override
    public void create() {
        float aspectRatio = (float) Gdx.graphics.getWidth() / (float)Gdx.graphics.getHeight();
        sceneCamera = new OrthographicCamera(100.0f, 100.0f / aspectRatio);
        screenCamera = new OrthographicCamera(32.0f, 32.0f / aspectRatio);

        batch = new SpriteBatch();

        screenFrameBuffer = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),true);
        screenTexture = new TextureRegion(screenFrameBuffer.getColorBufferTexture());
        screenTexture.flip(false, true);
        shapeRenderer = new ShapeRenderer();

        balls = new Ball[128];
        for(int i = 0; i < balls.length; ++i) {
            balls[i] = new Ball();
            balls[i].position.set(0, 0);
            balls[i].velocity.set(rnd(-4, 4), rnd(-4, 4));
            balls[i].size = rnd(1, 1);
            balls[i].color.set(rnd(0.5f, 1.0f), rnd(0.5f, 1.0f), rnd(0.5f, 1.0f), 1.0f);
        }
    }

    private void renderScene(Camera camera, Color background) {
        camera.update();
        Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        Gdx.gl.glClearColor(background.r, background.g, background.b, background.a);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
        shapeRenderer.setProjectionMatrix(camera.combined);
        shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        for(int i = 0; i < balls.length; ++i) {
            balls[i].render(shapeRenderer);
        }
        shapeRenderer.end();

    }

    @Override
    public void render() {
        float cs = 8.0f;
        for(int i = 0; i < balls.length; ++i)
            balls[i].update();

        if (Gdx.input.isKeyPressed(Input.Keys.LEFT))
            sceneCamera.position.x -= cs * Gdx.graphics.getDeltaTime();
        if (Gdx.input.isKeyPressed(Input.Keys.RIGHT))
            sceneCamera.position.x += cs * Gdx.graphics.getDeltaTime();
        if (Gdx.input.isKeyPressed(Input.Keys.UP))
            sceneCamera.position.y -= cs * Gdx.graphics.getDeltaTime();
        if (Gdx.input.isKeyPressed(Input.Keys.DOWN))
            sceneCamera.position.y += cs * Gdx.graphics.getDeltaTime();

        if (Gdx.input.isKeyPressed(Input.Keys.A))
            screenCamera.position.x -= cs * Gdx.graphics.getDeltaTime();
        if (Gdx.input.isKeyPressed(Input.Keys.D))
            screenCamera.position.x += cs * Gdx.graphics.getDeltaTime();
        if (Gdx.input.isKeyPressed(Input.Keys.W))
            screenCamera.position.y -= cs * Gdx.graphics.getDeltaTime();
        if (Gdx.input.isKeyPressed(Input.Keys.S))
            screenCamera.position.y += cs * Gdx.graphics.getDeltaTime();

        // Render to framebuffer, clear the background to DARK_GRAY
        screenFrameBuffer.begin();
        renderScene(screenCamera, Color.DARK_GRAY);
        screenFrameBuffer.end();

        // Render to window/screen, clear backgroun to BLACK
        renderScene(sceneCamera, Color.BLACK);

        // Draw the framebuffer's texture as a sprite using a normal SpriteBatch
        batch.setProjectionMatrix(sceneCamera.combined);
        batch.begin();
        batch.draw(screenTexture, -sceneCamera.viewportWidth / 2.0f,-sceneCamera.viewportHeight / 2.0f,0,0,screenCamera.viewportWidth,screenCamera.viewportHeight,1.0f, 1.0f, 0.0f);
        batch.end();

        // This just draws the outline of what the screen camera looks at
        shapeRenderer.setProjectionMatrix(sceneCamera.combined);
        shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
        shapeRenderer.setColor(Color.CYAN);
        shapeRenderer.rect(
                screenCamera.position.x - screenCamera.viewportWidth / 2.0f,
                screenCamera.position.y - screenCamera.viewportHeight / 2.0f,
                screenCamera.viewportWidth,
                screenCamera.viewportHeight

        );
        shapeRenderer.end();
    }

    @Override
    public void dispose() {
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}
Related