Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Surface shader multi compile with different transparency parameters

Discussion in 'Shaders' started by Arycama, May 3, 2017.

  1. Arycama

    Arycama

    Joined:
    May 25, 2014
    Posts:
    185
    Is it possible to use multi compile (Or shader feature) to generate variants of a surface shader with different transparency options?

    I have a surface shader which I want to have an alpha-blended option, and an opaque option that can be toggled by a keyword. All the surface/lighting calculations are the same, one just uses alpha blending.

    I have tried wrapping #pragma alpha:blend in an #if _ALPHABLEND_ON block, but it doesn't seem to work.
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    That doesn't work. Keywords can only be used inside the actual shader and not on the blending states for example. You can use a value to set the blending state though. Look at the source of the standard shader.
     
  3. Arycama

    Arycama

    Joined:
    May 25, 2014
    Posts:
    185
    I know I can use keywords to control blending inside a vert/frag shader, but I was wondering if there is a way to do it with surface shaders.

    From my understanding, the shader is meant to save you having to write out repetitive vert/frag shaders for all the different lighting modes (Vert, forward, deferred). It seems like it should also have a way to generate shaders with multiple transparency options as well, otherwise you're basically just copying/pasting all the code anyway and may as well just write vert/frag shaders from the start.
     
    Last edited: May 3, 2017
    morepixels likes this.
  4. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    You lost me a bit there. I assume you mean surface shader with standard shader. I wasn't talking about any specific "type" of shader. A surface shader gets converted into a fragment shader and the same rules apply. So like I said, keywords can't be used to switch the blending state, but you can use an integer to set it. For an example of this, look at the standard shader that comes with Unity. Keywords can be used to switch inside the code of the shader. That also goes for both fragment shaders and surface shaders.

    If you just want to prevent copy pasting, you can also use an include. That would be my way in this case. With different blending I also want a different render queue order, so I would make it a separate shader that reuses the same code from an include.
     
  5. Arycama

    Arycama

    Joined:
    May 25, 2014
    Posts:
    185
    Ah whoops, I meant to say surface shader, not standard shaders, my bad.
     
  6. Arycama

    Arycama

    Joined:
    May 25, 2014
    Posts:
    185
    With a vert/frag shader, setting the blend modes from script works fine, as long as you set the render queue as well. But to enable transparency in a surface shader, you need to use #pragma alpha:fade, alpha:blend etc, otherwise the surface shader isn't transparent, regardless of how you set the blending modes.
     
  7. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Ah, ok, I was under the assumption that you could just use Blend like in a fragment shader. Either way, a transparent shader should be a different shader than an opaque one in my opinion. To prevent copy pasting code, which is always bad, you can use includes.

    Edit: Come to think of it, I don't think I've ever used surface shaders for anything with transparency, since they are not affected by lighting anyway. So there is little need to use a surface shader over a fragment shader.
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,379
    I'm not sure it's possible. You can't defined #pragma alpha:fade by itself because it's a modifer of the #pragma surface, not a #pragma in of itself.

    It might be possible using #pragma surface alpha: premul and a finalcolor function, but the alpha:fade and alpha: premul modifiers hardcode the _ALPHABLEND_ON and _ALPHAPREMULTIPLY_ON keywords into the shader, so you can't reuse those to toggle between modes.

    The best option I can think of is using #pragma surface ... keepalpha and manually set the blend mode to Blend One OneMinusSrcAlpha. The ForwardAdd pass is automatically overridden with Blend One One if no "alpha:" modifier is set, and no _ALPHA keywords are set when using "keepalpha", but you would still need to use a finalcolor function to multiply the color by the alpha if you want to use alpha blending (aka alpha:fade), but premultiplied alpha (aka transparency) should "just work". You can control the top level blend mode and ZWrite from script using the same material parameter, just make sure you use Blend One OneMinusSrcAlpha for both fade and transparency.
     
  9. derrickMT

    derrickMT

    Joined:
    Apr 19, 2017
    Posts:
    2
    Thank you for the great write-up on the keepalpha approach. It does seem to work going this route as the shader now obeys whatever Blend options you set, including additive (One One).

    Why do you say to use Blend One OneMinusSrcAlpha for both "fade" and "transparent" modes? It seems from my brief experimenting that with keepalpha the shader uses whatever you set to o.Alpha in the surface function, and then uses whatever Blend options you set... so theoretically you can still set Blend, render tags, ZWrite, etc. from a script without needing a finalcolor function, unless there's something I'm missing?
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,379
    The original post is specifically about making a surface shader that can switch between "fade" and "transparent" blend modes with keywords. You can't modify the blend state or #pragma surface parameters with #if blocks, but you can replicate a Blend SrcAlpha OneMinusSrcAlpha with Blend One OneMinusSrcAlpha using an #if to enable and disable pre-multiplying the color by the alpha.

    Alternatively you could control the blend state directly with material properties, which is what Unity's Standard shader does behind the scenes with its custom material editor.