Post processing and the alpha channel


This is kinda related to Frame Buff Picking and Post Processing - Coding - PlayCanvas Discussion but since this is a different problem I figured I’d start a new post.

What we are doing is displaying a transparent playcanvas canvas in a html page that is placed on top of a html background image. The idea being that if we post process the playcanvas canvas the background graphic won’t be affected.

The problem is the post processing pipeline appears to be incorrectly displaying the alpha channel (a) depending on the highest value in the other channels (rgb).

For the examples below the background graphic is a mountain scene which resides on the html page. The playcanvas canvas is transparent and is displaying a 3D door. The overall effect is a door that appears to be floating in space in a mountain scene.

I simplified the brightness/contrast post processing script to clearly show the issue. With the following shader code:

“void main() {”,
“vec4 col=texture2D(uColorBuffer, vUv0);”,
“gl_FragColor = vec4(0, 0, 0, col.a);”,

I get the results I would expect. The door is pure black, the background of the canvas is transparent and you can see html mountain image behind it.

This code however:

“void main() {”,
“vec4 col=texture2D(uColorBuffer, vUv0);”,
“gl_FragColor = vec4(0.5, 0.5, 0.5, col.a);”,

Gives me a grey door as expected, but the surrounding pixels have an 50% alpha channel which is combining the grey color with the background html image visually blowing out the appearance of the background.

If I change the code to:

“void main() {”,
“vec4 col=texture2D(uColorBuffer, vUv0);”,
“gl_FragColor = vec4(1,1,1, col.a);”,

I would expect a white door with a transparent background, the mountain scene visible behind it. What I get instead is a solid white canvas covering up the html mountain image completely.

I tried other various settings and what appears to be happening is whatever the highest value in the r, g, or b channels is what the alpha channel is set to? for example, If the red channel has a 1 in it, then the alpha channel is set to 1 instead of whatever the original alpha value was.

What could be causing this? Is it a blend mode thing maybe?

Joe H.

Would it be possible to share an example project please? IIRC, you are using engine only so a github or zip would fine too.

What is the alpha blend mode you use to combine playcanvas output with the background? I suspect that needs to be changed to do what you need.

I’ll create a little example app and post it.

1 Like


I created an example app showing the problem. Super simple, loads your rotating box example with a transparent background. Also added a skybox which is hidden from the camera.

The slider effects the brightness. If you move the slider all the way to the left the box turns black as expected, if you move the slider all the way to the right it turns the whole canvas white.

Thanks for looking in to this, let me know what you find out.

Joe H.

1 Like


Did you guys get a chance to look at this, or have a sense of what a fix or workaround might look like?

Joe H.

Have you investigated the blend mode I mentioned? It seems the problem could be there, as from your screenshots it seems playcanvas gives you expected output.


Check out the example I sent. It appears to show a bug on the Playcanvas side?

The example is a simple application with post processing. Moving the slider hard right (setting the value of brightness > 0) turns the whole screen white. Moving the slider hard left (setting the value of brightness < 0) works as expected.

This is what I’m seeing?

This looks correct from what you’ve described before?

Yeah that looks right, what browser is that?

Chrome Version 95.0.4638.69 (Official Build) (arm64)

I just tried Edge, Firefox, and Chrome on Windows…all three are blowing out the brightness full screen when the slider is moved hard right

Interesting, it happens on Windows for me too on Chrome. Odd that it’s a Windows only bug :thinking:

Yeah, that’s what I am seeing. Our initial delivery platform is iOS tablets and phones (we will likely go to all the other platforms later, windows, etc.)

I uploaded the app to my server and checked it out on my iPad in Safari, same deal.

Post Processing Example (

EDIT: Just installed iPad Chrome, same deal there too. Seems like the only browser that works the way you would think is Mac Desktop Chrome?

Something to investigate on our side maybe as Chrome and Safari on my Mac isn’t rendering correctly from what I can see.

This line in the shader:

gl_FragColor.rgb += uBrightness;

On the transparent areas of scene, it’s taking the RGB of the clear colour of the camera and adding the brightness to it. So brightness of 1 will make the RGB of that area effectively (1, 1, 1, 0).

From what Martin is saying, the current blend mode of WebGL context of the canvas is probably doing an additive by default (correct me if I’m wrong Martin)?

A fix/workaround for this is to change the shader to take into account of the fragment alpha like so:

gl_FragColor.rgb += uBrightness * gl_FragColor.a;


Nice! I ended up adding the same code to the contrast stuff too because low contrast values were also washing out the screen. It all seems to be working? Here’s the new code:

“gl_FragColor = texture2D( uColorBuffer, vUv0 );”,

                "gl_FragColor.rgb += uBrightness * gl_FragColor.a;",

                "if (uContrast > 0.0) {",
                    "gl_FragColor.rgb = ((gl_FragColor.rgb - 0.5) / (1.0 - uContrast) + 0.5) * gl_FragColor.a;",
                "} else {",
                    "gl_FragColor.rgb = ((gl_FragColor.rgb - 0.5) * (1.0 + uContrast) + 0.5) * gl_FragColor.a;",

You guys rock. Thanks!
Joe H.


Not directly related to this project … but I was testing playcanvas rendering on top of camera feed, and had the same issue on iOS (have not tried Windows). The fix was to set camera clear color to 0,0,0,0 as that is ‘no-op’ for premultiplied alpha which browsers seems to use.

so I create my camera this was, and the over-brightening went away:

    camera.addComponent("camera", {
        clearColor: new pc.Color(0, 0, 0, 0)