0
votes

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.

1
You should look to glsl shader usage or particles.Fuat Coşkun
Note that setting glBlendFunc directly is overwritten by whatever blend function is assigned to SpriteBatch when you call batch.end(). It's not clear to me why white color is added. If you are using Gdx.gl.glBlendFunc(GL20.GL_DST_COLOR, GL20.GL_ZERO);, colors can only get darker since you are only multiplying. But maybe that's not the blend function you're actually using if your drawing the frame buffer's texture to the screen with a sprite batch.Tenfour04

1 Answers

0
votes

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);