Bloom not smooth

Started by
2 comments, last by Ashaman73 10 years ago

Hey guys,

Is there a way to make a smooth bloom ?

The more the radius is higher (X or Y), the less it will be smooth, it leave trails (looks like interlaced) as seen on the image (Y radius=13.37) :

2tdx.png

The blur shader looks like this (from qeffects-gl) :


static const char g_szVertexShader_BloomBlur[] =
"uniform vec4 Local0;\n"
"void main(void)\n"
"{\n"
"gl_TexCoord[0] = gl_MultiTexCoord0 + Local0;\n"
"gl_TexCoord[1] = gl_MultiTexCoord0 + Local0 * 2.0;\n"
"gl_TexCoord[2] = gl_MultiTexCoord0 - Local0;\n"
"gl_TexCoord[3] = gl_MultiTexCoord0 - Local0 * 2.0;\n"
"gl_Position = ftransform();\n"
"}\n";

static const char g_szFragmentShader_BloomBlur[] =
"#extension GL_ARB_texture_rectangle : enable\n"
"uniform sampler2DRect Texture0;\n"
"void main(void)\n"
"{\n"
"vec3 col1 = texture2DRect(Texture0, gl_TexCoord[0].xy).rgb;\n"
"vec3 col2 = texture2DRect(Texture0, gl_TexCoord[1].xy).rgb;\n"
"vec3 col3 = texture2DRect(Texture0, gl_TexCoord[2].xy).rgb;\n"
"vec3 col4 = texture2DRect(Texture0, gl_TexCoord[3].xy).rgb;\n"
"gl_FragColor.rgb = (col1 + col2 + col3 + col4) / 4.0;\n"
"gl_FragColor.a    = 1.0;\n"
"}\n";

Any ideas ?

Advertisement

You don't have enough samples for the blur size you're using. Either add more samples to fill in the gaps, or lower the step size.

Tip: typical bloom isn't computed in a single pass, as it's too expensive to get a wide radius with decent performance and quality. Instead, you should perform a blur on a couple of downscaled images, starting with the highest resolution one and using that result in the next (lower resolution) pass as the source texture. Then you composite the results from every pass at the end and apply that to the image as bloom.

Something like:

  • Perform bloom threshold if needed (which you might be doing already).
  • Blur to the first texture (1/2 or 1/4 resolution).
  • Blur to the second texture, using the previous result as input (1/8 resolution).
  • Continue for the # of passes you want (this depends on the radius you want to achieve). Each pass is half the resolution of the last.
  • Take all the textures and add them together (if you want more accurate results, energy conservation can be applied).

Well, I added more samples, the quality is a bit better.

The shader now look like this :


static const char g_szVertexShader_BloomBlur[] =
"#extension GL_ARB_texture_rectangle : enable\n"
"precision mediump float;\n"
"uniform vec4 Local0;\n"
"varying vec4 texCoord[16];\n"
"void main(void)\n"
"{\n"
"texCoord[0] = gl_MultiTexCoord0 + Local0;\n"
"texCoord[1] = gl_MultiTexCoord0 + Local0 * 1.250;\n"
"texCoord[2] = gl_MultiTexCoord0 + Local0 * 1.375;\n"
"texCoord[3] = gl_MultiTexCoord0 + Local0 * 1.500;\n"
"texCoord[4] = gl_MultiTexCoord0 + Local0 * 1.625;\n"
"texCoord[5] = gl_MultiTexCoord0 + Local0 * 1.750;\n"
"texCoord[6] = gl_MultiTexCoord0 + Local0 * 1.875;\n"
"texCoord[7] = gl_MultiTexCoord0 + Local0 * 2.000;\n"
"texCoord[8] = gl_MultiTexCoord0 - Local0;\n"
"texCoord[9] = gl_MultiTexCoord0 - Local0 * 1.250;\n"
"texCoord[10] = gl_MultiTexCoord0 - Local0 * 1.375;\n"
"texCoord[11] = gl_MultiTexCoord0 - Local0 * 1.500;\n"
"texCoord[12] = gl_MultiTexCoord0 - Local0 * 1.625;\n"
"texCoord[13] = gl_MultiTexCoord0 - Local0 * 1.750;\n"
"texCoord[14] = gl_MultiTexCoord0 - Local0 * 1.875;\n"
"texCoord[15] = gl_MultiTexCoord0 - Local0 * 2.000;\n"
"gl_Position = ftransform();\n"
"}\n";

static const char g_szFragmentShader_BloomBlur[] =
"#extension GL_ARB_texture_rectangle : enable\n"
"precision mediump float;\n"
"uniform sampler2DRect Texture0;\n"
"varying vec4 texCoord[16];\n"
"void main(void)\n"
"{\n"
"vec3 col1 = texture2DRect(Texture0, texCoord[0].xy).rgb;\n"
"vec3 col2 = texture2DRect(Texture0, texCoord[1].xy).rgb;\n"
"vec3 col3 = texture2DRect(Texture0, texCoord[2].xy).rgb;\n"
"vec3 col4 = texture2DRect(Texture0, texCoord[3].xy).rgb;\n"
"vec3 col5 = texture2DRect(Texture0, texCoord[4].xy).rgb;\n"
"vec3 col6 = texture2DRect(Texture0, texCoord[5].xy).rgb;\n"
"vec3 col7 = texture2DRect(Texture0, texCoord[6].xy).rgb;\n"
"vec3 col8 = texture2DRect(Texture0, texCoord[7].xy).rgb;\n"
"vec3 col9 = texture2DRect(Texture0, texCoord[8].xy).rgb;\n"
"vec3 col10 = texture2DRect(Texture0, texCoord[9].xy).rgb;\n"
"vec3 col11 = texture2DRect(Texture0, texCoord[10].xy).rgb;\n"
"vec3 col12 = texture2DRect(Texture0, texCoord[11].xy).rgb;\n"
"vec3 col13 = texture2DRect(Texture0, texCoord[12].xy).rgb;\n"
"vec3 col14 = texture2DRect(Texture0, texCoord[13].xy).rgb;\n"
"vec3 col15 = texture2DRect(Texture0, texCoord[14].xy).rgb;\n"
"vec3 col16 = texture2DRect(Texture0, texCoord[15].xy).rgb;\n"
"gl_FragColor.rgb = gl_FragColor.rgb / 16.0;"
"gl_FragColor.rgb = (col1 + col2 + col3 + col4 + col5 + col6 + col7 + col8 + col9 + col10 + col11 + col12 + col13 + col14 + col15 + col16) / 16.0;\n"
"gl_FragColor.a    = 1.0;\n"
"}\n";

The only thing is that I can't get past 16 arrays.

Your steps are similar to the one I have :

  • Make the screen darker
  • Blur the darkened texture to texture (setting a radius)
  • Render full screen (1/2 resolution)
  • Repeat # of times
  • Combine normal and blurred scenes (using glActiveTextureARB)
  • Render full screen

I'm not sure about enhancing the code. Do you have some examples ?

It's how it looks like from (rough) code :


    int screenWidth = m_3DViewport[2];
    int screenHeight = m_3DViewport[3];
    int blurTexWidth = m_3DViewport[2]>>1;
    int blurTexHeight = m_3DViewport[3]>>1;
 
DarkenShader->Bind();
DarkenShader->SetParameter4f( 0, m_varBloomDarkenPower, 0, 0, 0 );
RenderScaledFSQ( blurTexWidth, blurTexHeight, screenWidth, screenHeight );
 
blurshader->Bind();
 
for( i=0;i < m_varBloomNumSteps; i++ )
{
      blurShader->param4f..( 0, radius0, ... );
      renderFSQ( blurTexWidth, blurTexHeight );
      copysubimage2D( RECTANGLE_ARB, ..., WindowSize[y] - blurTexHeight, blurTexWidth, blurTexHeight );
 
      blurShader->param4f..( 0, radius1, ... );
      renderFSQ( blurTexWidth, blurTexHeight );
      copysubimage2D( RECTANGLE_ARB, ..., WindowSize[y] - blurTexHeight, blurTexWidth, blurTexHeight );
}
 
combineShader->Bind();
combineShader->param4..( 0, scale, 0, 0, 0 );
bindTex( RECTANGLEARB, screen );
activetexARB( GL_TEXTURE1_ARB );
bindtex( RECTANGLEARB, blurtex);
activetexARB( GL_TEXTURE0_ARB );
RenderFSQ( screenWidth, screenHeight );
 
combineShader->Unbind();

I'm not sure what newest games use to make a perfect high quality performances bloom :S


The only thing is that I can't get past 16 arrays.

Do not transfer all the attributes from the vertex shader to the fragment shader. Do the (indirect) texture access directly in the fragment shader. Something like this:


fragment:
vec3 col = texture2DRect(Texture0, texCoord[0].xy).rgb;
col += texture2DRect(Texture0, texCoord[0].xy + Local0 * <xyz> ).rgb;
...
col += texture2DRect(Texture0, texCoord[0].xy + Local0 * <xyz> ).rgb;
col *= 1.0/16.0;
..

Further on, you should use a gaussian blur, therefor you need the right kernel size and (constant) weights. Take a look here to get the concept behind a gaussian blur filter.

This topic is closed to new replies.

Advertisement