Trevor
Trevor

Reputation: 363

WebGL: Why does transparent canvas show clearColor color component when alpha is 0?

Here's a simple spike showing my problem:

<html>
<head>
    <title>Clear Color</title>
    <style type="text/css">
        #stage {
            background-color: rgba(127,127,127,1);
        }
    </style>
</head>
<body>
    <canvas id="stage" width="100", height="100"></canvas>
    <script type="text/javascript">
        var options = {
            alpha: true,
            premultipliedAlpha: true
        };
        var ctx = document.getElementById("stage").getContext("webgl", options);
        ctx.clearColor(1, 0, 0, 0);
        ctx.enable(ctx.BLEND);
        ctx.blendFuncSeparate(
            ctx.SRC_ALPHA, ctx.ONE_MINUS_SRC_ALPHA,
            ctx.ONE, ctx.ONE_MINUS_SRC_ALPHA
        );
        ctx.clear(ctx.COLOR_BUFFER_BIT);
    </script>
</body>
</html>

The blendFunc is this:
(sR*sA) + (dR*(1-sA)) = rR
(sG*sA) + (dG*(1-sA)) = rG
(sB*sA) + (dB*(1-sA)) = rB
(sA*1) + (dA*(1-sA)) = rA

...which translates to this (I think):
(1*0) + (0.5*(1-0)) = 0.5
(0*0) + (0.5*(1-0)) = 0.5
(0*0) + (0.5*(1-0)) = 0.5
(0*1) + (1.0*(1-0)) = 1.0

Why am I seeing a light pink canvas instead of just the grey that the CSS declares? The pink is obviously coming from my clearColor, but why is it showing the red when the alpha component is 0?

Upvotes: 4

Views: 4617

Answers (1)

user128511
user128511

Reputation:

WebGL by default requires your colors to use premultiplied alpha. 1,0,0,0 is an invalid color because rgb of (1,0,0) * alpha of (0) = 0,0,0 not 1,0,0 so it's an invalid color and the spec is undefined for invalid colors. The results will be different on different browsers.

You have a few options

  • Tell WebGL your colors are not premultiplied

    gl = canvas.getContext("experimental-webgl", { premultipliedalpha: false });
    
  • Get rid of the alpha channel

    gl = canvas.getContext("experimental-webgl", { alpha: false });
    
  • Premultiply your alpha

    var r = 1;
    var g = 0;
    var b = 0;
    var a = 0;
    gl.clearColor(r * a, g * a, b * a, a);
    

    similarly in your shaders either pass in premultiplied colors or premultiply at the end.

    gl_FragColor = vec4(color.rgb * color.a, color.a);
    

Your blending settings have no effect. They only used when you call gl.drawXXX, they are not used when the canvas is composited with the background.

Upvotes: 6

Related Questions