10

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?

Jack Ukleja
  • 12,190
  • 10
  • 65
  • 101
vanmelle
  • 1,465
  • 1
  • 12
  • 21

3 Answers3

24

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.

vanmelle
  • 1,465
  • 1
  • 12
  • 21
-1

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/

Jack Ukleja
  • 12,190
  • 10
  • 65
  • 101
  • Notice I said " – vanmelle Feb 05 '10 at 20:50
  • Are you able to debug the actual values output by the shader? From the sample you show I cannot see how this could be failing. You are just copying the alpha. What image formats are you using? What happens to the alpha without your shader? – Jack Ukleja Feb 05 '10 at 23:05
-1

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,

  • Thanks, but I don't know what that means for WPF, or if WPF even has such a thing. I've read elsewhere that WPF doesn't have support for all the directX blend modes. – vanmelle Feb 10 '10 at 22:18