There's an entity of my LibGDX game I would like to render to a PNG. So I made a small tool that is a LibGDX app to display that entity and it takes a screenshot on F5. The goal of that app is only to generate the PNG.
camera.update();
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
animation.update(Gdx.graphics.getDeltaTime() * 1000);
animation.draw(batch);
batch.end();
if(exporting)
// export...
From that wiki page I found out how to make a screenshot and by removing the for loop, I was able to get a screenshot that doesn't replace transparent pixels by black pixels.
byte[] pixels = ScreenUtils.getFrameBufferPixels(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), true);
Pixmap pixmap = new Pixmap(Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), Pixmap.Format.RGBA8888);
BufferUtils.copy(pixels, 0, pixmap.getPixels(), pixels.length);
PixmapIO.writePNG(Gdx.files.external("mypixmap.png"), pixmap);
pixmap.dispose();
It works well for the edges of the entity but not for the multiple parts inside.
Edges: (perfect)
Inside: (should not be transparent)
So I started playing with blending to fix that. With
batch.enableBlending();
batch.setBlendFunction(
exporting ? GL20.GL_ONE : GL20.GL_SRC_ALPHA, // exporting is set to true on the frame where the screenshot is taken
GL20.GL_ONE_MINUS_SRC_ALPHA);
This improved it a bit:
But with images like glasses that are supposed to be transparent, it's opaque:
Instead of:
Any idea of what I should do to fix this? What I want is pretty standard, a transparent background with semi transparent images on top of it. I want it to behave just like a regular image software would with layers (like GIMP).
Your issue is because written colors and alpha are both modulated by same function : SRC_ALPHA and ONE_MINUS_SRC_ALPHA.
You need to use glBlendFuncSeparate to achieve this. In your case :
batch.begin();
// first disable batch blending changes (see javadoc)
batch.setBlendFunction(-1, -1);
// then use special blending.
Gdx.gl.glBlendFuncSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA,GL20.GL_ONE, GL20.GL_ONE);
... your drawings ...
batch.end();
In this way, colors channels still blended as usual but alpha channels are added (both source and destination).
Note that with libgdx 1.9.7+, the batch blending hack is not required anymore and could be :
batch.begin();
batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA,GL20.GL_ONE, GL20.GL_ONE);
... your drawings ...
batch.end();
There are some limitations in some cases though, please take a look at my GIST for more information.