Search code examples
androidlibgdxopengl-es-2.0

Libgdx Stencil & ShapeRenderer


I am trying to accomplish something like this:

sample image

The whole screen will be black, then the insides of the triangle shape are the parts that will only appear.

I tried using SCISSOR but it is rectangle in shape.

*Original Image Source: https://www.html5rocks.com/static/images/screenshots/casestudies/onslaught/controls_tutorial.png


Solution

  • There are a few different ways that you can render a masked image. One possible way is to use the depth buffer. I've written a small method that shows the process of setting up the buffer using a ShapeRenderer to define a triangular region of the image to render and mask out the remainder. The triangle mask could be replaced by any other shape that the ShapeRenderer is capable of rendering.

    // For a 2D image use an OrthographicCamera
    OrthographicCamera cam = new OrthographicCamera();
    ShapeRenderer shapes = new ShapeRenderer();
    cam.setToOrtho(true, screenWidth, screenHeight);
    shapes.setProjectionMatrix(cam.combined);
    
    private void renderStencilImage(float runTime){
        // Clear the buffer
        Gdx.gl.glClearDepthf(1.0f);
        Gdx.gl.glClear(GL30.GL_DEPTH_BUFFER_BIT);
        // Disable writing to frame buffer and
        // Set up the depth test
        Gdx.gl.glColorMask(false, false, false, false);
        Gdx.gl.glDepthFunc(GL20.GL_LESS);
        Gdx.gl.glEnable(GL20.GL_DEPTH_TEST);
        Gdx.gl.glDepthMask(true);
        //Here add your mask shape rendering code i.e. rectangle
        //triangle, or other polygonal shape mask
        shapes.begin(ShapeRenderer.ShapeType.Filled);
        shapes.setColor(1f, 1f, 1f, 0.5f);
        shapes.triangle(x1,y1,x2,y2,x3,y3);
        shapes.end();
        // Enable writing to the FrameBuffer
        // and set up the texture to render with the mask
        // applied
        Gdx.gl.glColorMask(true, true, true, true);
        Gdx.gl.glDepthMask(true);
        Gdx.gl.glDepthFunc(GL20.GL_EQUAL);
        // Here add your texture rendering code
        batcher.begin();
        renderFrame(runTime);
        batcher.end();
        // Ensure depth test is disabled so that depth
        // testing is not run on other rendering code.
        Gdx.gl.glDisable(GL20.GL_DEPTH_TEST);
    }
    

    Before you call the method, you must first create a ShapeRenderer and set the projection matrix. You must also set the depth buffer option in the android config in the onCreate method like this:

    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
        config.depth = 15;
        initialize(new game(), config);
    }
    

    The options for glDepthFunc define how the mask is applied to the texture. Check out the OpenGL wiki to see the arguments that can be passed to the function.