Bloom and Anti Aliasing banding

I am trying to add a glowing effect to my scene. As far as I know the best way to do is with the a bloom filter using the EffectComposer. Unfortunately using the EffectComposer negates the beautiful anti-aliasing that comes with the renderer. I added a SSAARenderPass but it causes banding even with unbiased set to true, and sampleLevel to 32. See attached pictures below.

I ran across this discussion dealing with a similiar issue: Effect Composer Gamma Output Difference - #23 by Mugen87 and I believe I integrated the discussed solution, by explicitly creating a RenderTarget for the EffectComposer that has type set to THREE.FloatType. This definitely helped, but I still have some pretty noticeable banding.

How can I have a glowing effect and preserve a clean render without aliasing or banding?

const pixelRatio = renderer.getPixelRatio();
const renderScene = new RenderPass( scene, camera );
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
bloomPass.threshold = 0.9;
bloomPass.strength = 1.5;
bloomPass.radius = 0.15;

var ssaaRenderPass = new SSAARenderPass( scene, camera );
ssaaRenderPass.sampleLevel = 32;
ssaaRenderPass.unbiased = true;

var adaptToneMappingPass = new AdaptiveToneMappingPass(true, 256);
var gammaCorrectionPass = new ShaderPass( GammaCorrectionShader );

var renderTarget = new THREE.WebGLRenderTarget( window.innerWidth * pixelRatio, window.innerHeight * pixelRatio, 
{
    minFilter: THREE.LinearFilter,
    magFilter: THREE.LinearFilter,
    format: THREE.RGBAFormat,
    stencilBuffer: false,
    type: THREE.FloatType
});
renderTarget.texture.name = 'EffectComposer.rt1';

composer = new EffectComposer(renderer, renderTarget);
composer.addPass(renderScene);
composer.addPass(ssaaRenderPass); //Seems to be better than fxaa but has terrible banding
composer.addPass(adaptToneMappingPass);
composer.addPass(bloomPass);
composer.addPass(gammaCorrectionPass);

This is with no SSAA or Bloom. No banding but terrible aliasing

This is with Bloom but no SSAA. No banding in the background, though there is in the bloom effect

This is with both Bloom and SSAA. The aliasing is better but the banding in the background and bloom is bad

2 Likes

I struggled with the same problem for a while but found out a solution after doing some research with the post processing source code. In case someone else is still facing this issue here is what I found:

After taking a look at the source code for SSAAPass and UnrealBloomPass, you can see that the the passes create new WebGLRenderTargets in the SSAA render function and in the constructor for the bloom effect. However the constructor parameters don’t contain type information so basically they will use the default as seen in the Texture class. Basically this will cause the passes to be rendered with poorer accuracy.

To fix SSAA banding, create a new render target and assign it to the pass before any rendering so the pass won’t create the default RT.

An example:

    const ssaaPars = { 
        minFilter: THREE.LinearFilter, 
        magFilter: THREE.LinearFilter, 
        format: THREE.RGBAFormat, 
        type: THREE.FloatType 
    };

	const ssaaRT = new THREE.WebGLRenderTarget( DEFAULT_WIDTH, DEFAULT_HEIGHT, ssaaPars);
    this.ssaaPass.sampleRenderTarget = ssaaRT;

To fix bloom banding, you have to do the same trick but since UnrealBloomPass is constructed a bit differently, you can just change the RT types after the pass has been constructed without having to create brand new ones.

An example:

    this.bloomPass.renderTargetsHorizontal.forEach(element => {
        element.texture.type = THREE.FloatType;
    });
    this.bloomPass.renderTargetsVertical.forEach(element => {
        element.texture.type = THREE.FloatType;
    });

I guess the post processing passes could be updated a bit so that they take the type from the composer, or have the types parametrized in the constructor unless someone knows a better solution to change the RT types for the passes.

1 Like

Are you aware that this is extremely costly and consumes massive memory then? There are other ways to fix banding issues without using 32bit per channel.

Indeed using 32bit float is a bit of an overkill and doesn’t really add much, I found that HalfFloatType is virtually identical to using FloatType.

I couldn’t figure out any other straightforward way to fix banding than increase memory allocation. Is there some approach that I’ve missed?

Can’t look into it now but you should first make sure the render setup is right regarding color encording, and also make sure you’re not using parameters ramping stuff up causing them to go above 1 or below 0.