Search Unity

Understanding transparency?

Discussion in 'Shaders' started by UnbridledGames, Dec 22, 2020.

  1. UnbridledGames

    UnbridledGames

    Joined:
    May 12, 2020
    Posts:
    139
    Very new to writing shaders. Lots of experience in C/C++/C#, never written a shader before, but I've poked through the code.

    I've been wanting a scanlines-like effect on some text, and none of the scanlines tools or shaders I have work on TextMeshPro text. Today I decided to dive in and figure it out myself. Looking at the Unity tutorials and the TMP_SDF-Mobile shader source, and playing with some examples, I figured out how to do it. Basically took the simple shader code for the scanline shader I'm already using, made a copy of TMP_SDF-Mobile, and pasted it into the end of the fragment shader code. Got it to work great!

    Now I'm trying to add some opacity to the scanlines, so what's behind the letters shows through. And I'm running into something really weird. If I lower the alpha to make it transparent, only LIGHTER objects behind it show through. If the text is over a white box, the alpha "works" and white shows through the letters. If the letters are over a black box, the RGB color of the scanlines shows instead.

    I messed around a bit and put the transparency code into the vertex function instead of the fragment function, and that sort of worked, but the results were wonky.

    Can someone explain how to apply transparency correctly in a shader so that whatever is behind it shows through? I'm clearly missing something, but any tutorials I'm seeing that might apply are way over my head at the moment.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    The TMP shaders are using premultiplied alpha blending. The short version of what that means is if you just reduce the alpha, it becomes an additive shader. If you want it to fade out, you want to reduce both the alpha and the color value. If you're multiplying the alpha value, multiply the color at the same time.
     
  3. UnbridledGames

    UnbridledGames

    Joined:
    May 12, 2020
    Posts:
    139
    Fantastic! Thank you. By setting the scanlines to float4(0,0,0,0) instead of just darkening the existing text, it made them completely transparent. I'm used to transparency just being, well, transparent (or more accurately for my case, translucent).

    If it's not too much trouble, and if you have time, why are the colors blending like this? With all the work I've done designing UI elements, photoshop over the years etc, when you lower something's opacity, it wholly becomes more transparent. Whereas here, it's only becoming transparent if a "brighter" color is behind it. What's happening thats making it additive?

    Regardless, thanks for your help. I can get this to work correctly now I believe.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    This is traditional alpha blending. For shaders that blend mode would look like this:
    Code (csharp):
    1. Blend SrcAlpha OneMinusSrcAlpha
    So, what does that mean? That’s describing to the GPU what math to use in the blend function. The blending function is always:
    SrcColor * SrcFactor + DstColor * DstFactor


    Where
    SrcColor
    is the output of the “source” (the shader), and
    DstColor
    is the “destination” (the color previously in the render target). The factors are what the
    Blend
    is setting, and are quite literal. So the above traditional alpha blending blend looks like this:
    Code (csharp):
    1. SrcColor * SrcAlpha + DstColor * OneMinusSrcAlpha
    or if written in HLSL would look like this:
    Code (csharp):
    1. return (srcColor.rgba * srcColor.a) + (dstColor.rgba * (1.0 - srcColor.a));
    That also happens to be a linear interpolation, aka
    lerp(dst, src, alpha)
    . But basically as the alpha changes, you get more or less of either the
    Src
    or
    Dst
    color in the end. As you would expect.


    So what about what the TMP shader was using?
    Blend One OneMinusSrcAlpha

    That's premultiplied alpha blending, somewhat confusingly named since it basically refers to the fact the color is multiplied by the alpha prior to output. Basically it assumes you've done the
    SrcColor * SrcAlpha
    in the shader already before outputting it. So why would TextMesh Pro use that instead of traditional alpha blending? Well, when doing any kind of compositing, you want the values to be multiplied by the alpha value before blending between them to get correct results. Since the TMP shader is doing things like outlines and drop shadows with variable opacity, the final in-shader result is already "premultiplied", so it just makes sense to use that as the blend instead of trying to reconstruct the un-multiplied color value.

    It also has the advantage of being able to be both additive and alpha blended in the same shader.