10
votes

Is there something unusual about how the alpha component is handled in a pixel shader? I have a WPF application for which my artist is giving me grayscale images to use as backgrounds, and the application colorizes those images according to the current state. So I wrote a pixel shader (using the WPF Pixel Shader Effects Library infrastructure) to use as an effect on an Image element. The shader takes a color as a parameter, which it converts to HSL so it can manipulate brightness. Then for each grey pixel, it computes a color whose brightness is interpolated between the color parameter and white in proportion to the brightness of the source pixel.

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float4 src = tex2D(implicitInputSampler, uv);

    // ...Do messy computation involving src brightness and color parameter...

    float4 dst;
    dst.r = ...
    dst.g = ...
    dst.b = ...
    dst.a = src.a;
    return dst;
}

This works just fine on the pixels where alpha = 1. But where alpha = 0, the resultant pixels come out white, rather than having the window's background show through. So I made a tiny change:

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float4 src = tex2D(implicitInputSampler, uv);
    if (src.a == 0) 
        return src;
    ...

and now the transparent parts really are transparent. Why? Why didn't the dst.a = src.a statement in the first version accomplish that? Unfortunately, even this is only a partial fix, because it looks to me like the pixels with 0 < alpha < 1 are coming out white.

Does anyone know what I'm not understanding about alpha?

3

3 Answers

24
votes

After some more web searching, I discovered the piece I was missing.

According to an article on MSDN: "WPF uses pre-multiplied alpha everywhere internally for a number of performance reasons, so that's also the way we interpret the color values in the custom pixel shader."

So the fix turns out to be to throw in a multiplication by alpha:

float4 main(float2 uv : TEXCOORD) : COLOR
{
    ...
    dst.rgb *= src.a;
    return dst;
}

And now my output looks as I expect it to.

-1
votes

0 < alpha < 1 are coming out white

What ranges are you expecting here?

All values are going to be in the range 0.0 and 1.0... pixel shaders do not work in discrete 256 colour ranges, they are floating point where 1.0 is the maximum intensity.

If your calculations end up setting r/g/b values to >1.0 you are going to get white...

http://www.facewound.com/tutorials/shader1/

-1
votes

Dude I am working on a XNA game, and I had to use a grayscale pixel shader and I got the same problem you are facing. I donno if you are familiar with XNA environment or not, but I solved the problem by changing the SpriteBatch drawing SpriteBlendMode from SpriteBlendMode.None to SpriteBlendMode.AlphaBlend, I hope this can help you knowing the reason.

regards,