Bloom effect

Hi,

Looking at adding a bloom effect to my game. Is it possible to do this without using 3 FBOs?

First FBO used for original render, the second and third FBO will be used for the two passes of the Gaussian blur operation.

Many thanks,

It is not possible to do bloom without using FBOs. Why are you asking?

Just learning how to do it.

I take it the FBO’s need updating each time the game screen is scrolled and you set the FBO’s to the size of the screen? Just wondering
how many cpu cycles it takes to do the bloom effect.

Thanks

You have a couple of misunderstandings here.

An FBO does not contain any actual data. What an FBO does is allow you to draw things to a texture. The FBO itself has no actual data; it just tells OpenGL where to put the result of your pixel shader at the end.

Secondly, using an FBO takes very little CPU cycles. The actual gaussian blur shader and the writing to the texture all happens in your graphics card, so it doesn’t actually cost any CPU cycles at all (as long as your GPU is fast enough and doesn’t lag behind, forcing the CPU to wait for it to catch up).

Many thanks,

Logic for a blur effect:

  1. Create two FBO’s the size of original display/scene
  2. Render original scene with default shader to FBO_A
  3. Enable horizontal blur shader, render FBO_A to FBO_B
  4. Enable vertical blur shader, render FBO_B to screen

I’m taking it the original display size is what the camera sees, not the whole game/world scene!

Hoping to try this tonight.

Thanks

Hi,

Ok, tried to do just blur effect first, fine when doing it just for my hud display, great I thought, just move it around so it will do it for the landscape, no landscape is shown :frowning:


blurTargetA.begin();
		
		Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1f);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
					
		batch.setShader(null);
			
		batch.begin();
		batch.setProjectionMatrix(camera.combined);
	
		// Draw the map - only draw what camera sees
		worldMap.drawMap(debug, batch, regions, camX-2 , camY-2, endX , endY+2, WORLDWIDTH, WORLDHEIGHT);
	
		batch.flush();
		blurTargetA.end();
	
		batch.setShader(blurShader);
		// pass 1 - horizontal blur
		blurShader.setUniformf("dir", 1f, 0f);
		blurShader.setUniformf("radius", 1 * MAX_BLUR);
		blurTargetB.begin();
		fboRegion.setTexture(blurTargetA.getColorBufferTexture());
		batch.draw(fboRegion, 0, 0);
		batch.flush();
		blurTargetB.end();
			
		// pass 2 - vertical blur
		blurShader.setUniformf("dir", 0f, 1f);
		blurShader.setUniformf("radius", 1 * MAX_BLUR);
		fboRegion.setTexture(blurTargetB.getColorBufferTexture());
		batch.draw(fboRegion, 0, 0);
		
		batch.setShader(null);
		batch.end();

and the drawMap method:


public void drawMap(boolean bDebug, SpriteBatch batch, TextureRegion[][] region, int sx, int sy, int ex, int ey,
			int w, int h) {

		// draw what we can see
		for (int row = sy; row < ey + 1; row++)
			for (int col = sx; col < ex + 1; col++) {
				BlankEntity entity = getEntity(col, row);
				if (entity != null) {
					{
						batch.setColor((float) entity.lightValue / MAXLIGHT, (float) entity.lightValue / MAXLIGHT,
								(float) entity.lightValue / MAXLIGHT, 1);
						
						if (entity.lightValue > 0)
							worldMap[col][row].draw(batch, region, col, row);
											}
				}
			}

	}

I thought it had to be the line worldMap[col][row].draw(batch, region, col, row); as col and row basically start from where the camera is and thus thought by subtracting sx from col and sy from row would fix the issue as it is now placed in a FBO, but still nothing shows. I’m convinced it got to be something to do with this.

The shader does work as originally did it with my HUD and worked a treat. I’m stumped, but still have a feeling it is the drawMap method.

BTW I originally set the FBO’s etc up like this:


			blurTargetA = new FrameBuffer(Format.RGBA8888, SCREENWIDTH, SCREENHEIGHT, false);
			blurTargetB = new FrameBuffer(Format.RGBA8888, SCREENWIDTH, SCREENHEIGHT, false);
			fboRegion = new TextureRegion(blurTargetA.getColorBufferTexture());
			fboRegion.flip(false, true);


SCREENWIDTH is 1920 and SCREENHEIGHT is 1080.

Hope somebody can see what I’m obviously doing wrong here - new to using shaders, so please be easy on me!

Many thanks you amazing people ;D

PS - Vertex and pixel shader shown here:



attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 vColor;
varying vec2 vTexCoord;

void main() {
	vColor = a_color;
	vTexCoord = a_texCoord0;
	gl_Position = u_projTrans * a_position;		
}


varying vec4 vColor;
varying vec2 vTexCoord;

//declare uniforms
uniform sampler2D u_texture;
uniform float resolution;
uniform float radius;
uniform vec2 dir;

void main() {
    //this will be our RGBA sum
    vec4 sum = vec4(0.0);

    //our original texcoord for this fragment
    vec2 tc = vTexCoord;

    //the amount to blur, i.e. how far off center to sample from 
    //1.0 -> blur by one pixel
    //2.0 -> blur by two pixels, etc.
    float blur = radius/resolution; 

    //the direction of our blur
    //(1.0, 0.0) -> x-axis blur
    //(0.0, 1.0) -> y-axis blur
    float hstep = dir.x;
    float vstep = dir.y;

    //apply blurring, using a 9-tap filter with predefined gaussian weights

    sum += texture2D(u_texture, vec2(tc.x - 4.0*blur*hstep, tc.y - 4.0*blur*vstep)) * 0.0162162162;
    sum += texture2D(u_texture, vec2(tc.x - 3.0*blur*hstep, tc.y - 3.0*blur*vstep)) * 0.0540540541;
    sum += texture2D(u_texture, vec2(tc.x - 2.0*blur*hstep, tc.y - 2.0*blur*vstep)) * 0.1216216216;
    sum += texture2D(u_texture, vec2(tc.x - 1.0*blur*hstep, tc.y - 1.0*blur*vstep)) * 0.1945945946;

    sum += texture2D(u_texture, vec2(tc.x, tc.y)) * 0.2270270270;

    sum += texture2D(u_texture, vec2(tc.x + 1.0*blur*hstep, tc.y + 1.0*blur*vstep)) * 0.1945945946;
    sum += texture2D(u_texture, vec2(tc.x + 2.0*blur*hstep, tc.y + 2.0*blur*vstep)) * 0.1216216216;
    sum += texture2D(u_texture, vec2(tc.x + 3.0*blur*hstep, tc.y + 3.0*blur*vstep)) * 0.0540540541;
    sum += texture2D(u_texture, vec2(tc.x + 4.0*blur*hstep, tc.y + 4.0*blur*vstep)) * 0.0162162162;

    //discard alpha for our simple demo, multiply by vertex color and return
    gl_FragColor = vColor * vec4(sum.rgb, 1.0);
}

Blur filter I did ages ago, now need to put in bloom. I currently have to fbo’s, do I need another one to do the bloom? I’m taking it you do a threshold filter before the blur?

This is my code to do blur:


blurTargetA.begin();
			Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		
			batch.begin();

			worldMap.drawMapWithBloom(debug, batch, regions, camX, camY, endX, endY, WORLDWIDTH, WORLDHEIGHT);
			
			batch.flush();
			blurTargetA.end();
			
			batch.setShader(blurShader);// blurShader);
			// pass 1 - horizontal blur
			blurShader.setUniformf("dir", 1f, 0f);
			blurShader.setUniformf("radius", 1 * MAX_BLUR);

			blurTargetB.begin();

			Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

			fboRegion.setTexture(blurTargetA.getColorBufferTexture());

			batch.setColor(1, 1, 1, 1); // VIP!

			batch.draw(fboRegion, 0, 0, SCREENWIDTH, SCREENHEIGHT);
			batch.flush();
			blurTargetB.end();

			blurShader.setUniformf("dir", 0f, 1f);
			blurShader.setUniformf("radius", 1 * MAX_BLUR);
			fboRegion.setTexture(blurTargetB.getColorBufferTexture());

			batch.setProjectionMatrix(camera.combined); // bind to main camera
														// projection - VIP!!!

			batch.draw(fboRegion, (camX * 16), (camY * 16));// , 320,480);draw
															// to position
															// x400,y400

Where would I put code to do the threshold and then the bloom?

Thanks

Hi,

Ok, so bloom(threshold) shader just goes in first pass (when copying screen to first frame buffer).

Bloom shader looks like this:



uniform sampler2D u_texture0;
uniform sampler2D u_texture1;
uniform float BloomIntensity;
uniform float OriginalIntensity;
varying MED vec2 vTexCoord;

void main()
{
	vec4 original = texture2D(u_texture0, vTexCoord) * OriginalIntensity;
	vec4 bloom = texture2D(u_texture1, vTexCoord) * BloomIntensity; 	
        original = original *  (vec4(1.0) - bloom);	 	
 	gl_FragColor =  original + bloom; 	
}

Now, my issue is, I light up parts of my display when player turns their torch on, but as OriginalIntensity is set only once, how can I get the
textures new colour? Thus, using torch as on, lights up parts of the screen but then using bloom, it’s like the torch isn’t on…

Any help is much appreciated!

Thanks

Off topic, but why the heck are you using my texture banking convention D: u_texture

You don’t own anything like u_texture - that just means it’s a uniform. It’s a typical naming convention. Not everything you “make up” is yours.

Steve, from what you’re saying the bloom isn’t getting applied? Or is messing with the existing lighting? A screenshot would be useful here.

Awe cute someone is angry at me sharing similarities with someone.

?!

Chrislo - Will get some screen shots up this evening. I do believe the bloom is being applied as it does brighten certain areas up.

I’ve posted some screen shots here:

https://sites.google.com/site/sterrialand/development/news/shaderwhoes

My code for doing bloom:


	if (bloomShaderOn) {

			blurTargetA.begin();
			Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		
			batch.begin();

			batch.setShader(bloomShader);
	     bloomShader.setUniformi("u_texture0", 0);
			bloomShader.setUniformi("u_texture1", 1);
			bloomShader.setUniformf("BloomIntensity", 2.5f);
			bloomShader.setUniformf("OriginalIntensity", 0.2f);
			Gdx.gl.glDisable(GL20.GL_BLEND);
			
			worldMap.drawMapWithBloom(debug, batch, regions, camX, camY, endX, endY, WORLDWIDTH, WORLDHEIGHT);
			
			batch.flush();
			blurTargetA.end();
			
			Gdx.gl.glEnable(GL20.GL_BLEND);	
			
			batch.setShader(blurShader);
			// pass 1 - horizontal blur
			blurShader.setUniformf("dir", 1f, 0f);
			blurShader.setUniformf("radius", 0.5f * MAX_BLUR);

			blurTargetB.begin();

			Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

			fboRegion.setTexture(blurTargetA.getColorBufferTexture());

			batch.setColor(1, 1, 1, 1); 

			batch.draw(fboRegion, 0, 0, SCREENWIDTH, SCREENHEIGHT);
			batch.flush();
			blurTargetB.end();

			blurShader.setUniformf("dir", 0f, 1f);
			blurShader.setUniformf("radius", 0.5f * MAX_BLUR);
			fboRegion.setTexture(blurTargetB.getColorBufferTexture());

			batch.setProjectionMatrix(camera.combined); // bind to main camera
														// projection - VIP!!!

			batch.draw(fboRegion, (camX * 16), (camY * 16));
												
			batch.setShader(null);
   }


FBO’s created like this:


			blurTargetA = new FrameBuffer(Format.RGBA8888, SCREENWIDTH, SCREENHEIGHT, false);
			blurTargetB = new FrameBuffer(Format.RGBA8888, SCREENWIDTH, SCREENHEIGHT, false);
			fboRegion = new TextureRegion(blurTargetA.getColorBufferTexture());

Thanks