I am writing a RTS Game for Android and I want the "Fog of War" effect on the player's units. This effect means that only a "circle" around each unit shows the background map while on places where no player unit is located, the screen should be black. I don't want to use shaders.
I have a first version of it working. What I am doing is to render the map to the default framebuffer, then I have a second Framebuffer (similar to light technics) which is completely black. Where the units of the players are, I then batch-draw a texture which is completely transparent and has a white circle with blurred edges in its middle. Finally I draw the second (light) FrameBuffer's colorTexture over the first one using Gdx.gl.glBlendFunc(GL20.GL_DST_COLOR, GL20.GL_ZERO);
The visual effect now is that indeed the whole map is black and a circle around my units is visible - but a lot of white color is added. The reason is pretty clear as I drew the light textures for the units like this:
lightBuffer.begin();
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE);
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.setColor(1f, 1f, 1f, 1f);
for (RTSTroopAction i : unitList) {
batch.draw(lightSprite, i.getX() + (i.getWidth() / 2) - 230, i.getY() + (i.getHeight() / 2) - 230, 460, 460); //, 0, 0, lightSprite.getWidth(), lightSprite.getHeight(), false, true);
}
batch.end();
lightBuffer.end();
However, I don't want the "white stuff" on the original texture, I just want the original background shine through. How can I achieve that ?
I think it's playing around with the blendFuncs, but I was not able to figure out which values to use yet.
Thanks to Tenfour04 pointing into the right direction, I was able to find the solution. First of all, the problem is not directly within batch.end();. The problem is, that indeed a sprite batch maintains its own blendFunc Settings. These get applied when flush(); is called. (end() calls it also ). However the batch is also calling flush when it draws a TextureRegion that is bound to a different texture than the one used in the previous draw() call.
So in my original code: whatever blendFunc I had set was always overridden when I called batch.draw(lightBuffer,...). The solution is to use the spritebatch's blendFunc and not the Gdx.gl.blendFunc.
The total working code finally looks like this:
lightBuffer.begin();
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glEnable(GL20.GL_BLEND);
// start rendering to the lightBuffer
// set the ambient color values, this is the "global" light of your scene
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// start rendering the lights
batch.setProjectionMatrix(camera.combined);
batch.begin();
// set the color of your light (red,green,blue,alpha values)
batch.setColor(1f, 1f, 1f, 1f);
for (RTSTroopAction i : unitList) {
if (i.getOwnerId() == game.getCallback().getPlayerId()) {
batch.draw(lightSprite, i.getX() + (i.getWidth() / 2) - 230, i.getY() + (i.getHeight() / 2) - 230, 460, 460); //, 0, 0, lightSprite.getWidth(), lightSprite.getHeight(), false, true);
}
}
batch.end();
lightBuffer.end();
// now we render the lightBuffer to the default "frame buffer" // with the right blending !
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
batch.setProjectionMatrix(getStage().getCamera().combined);
batch.enableBlending();
batch.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
batch.begin();
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
batch.draw(lightBufferRegion,0, 0, getStage().getWidth(), getStage().getHeight());
batch.end();
batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);