Search Unity

Surface shaders and blending (ForwardAdd LightMode)

Discussion in 'Shaders' started by abeck99, Jul 25, 2014.

  1. abeck99

    abeck99

    Joined:
    May 9, 2014
    Posts:
    6
    I'm trying to get a mesh rendering with a normal map, but the tris on my mesh are blending in a strange way. A picture is worth a thousand words so you can see what's happening below:
    TransparentAlice.PNG

    I used #pragma debug to track down the problem and it seems that the problem is the ForwardAdd pass is blending with Blend One One and changing it to Blend SrcAlpha OneMinusSrcAlpha blends correctly, but copying and pasting the debug shader as a new shader only works with one light and doesn't take into account ambient light.

    Attached is the shader code and screenshot of what my mesh looks like (all on a single plane).

    Thanks!
     

    Attached Files:

  2. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    ForwardAdd is supposed to render with One One - it's an additive pass that goes on top of ForwardBase.

    Don't specify the blend modes for surface shaders unless you really need them to be something specific - they'll be set up correctly for you by default.

    Also, I'd not use ZWrite Off, because as far as I can tell, you'll want this to write to the ZBuffer.

    So, strip out these lines
    Code (csharp):
    1.         ZWrite Off
    2.         Blend SrcAlpha OneMinusSrcAlpha
    3.         BlendOp Add
    And add this to the Tags
    Code (csharp):
    1. "Queue" = "TransparentCutout"
    And then you've got both "alpha" AND "alphatest" in your #pragma surface... you're wanting to just have alphatest I think.
     
  3. abeck99

    abeck99

    Joined:
    May 9, 2014
    Posts:
    6
    I made all the changes and it didn't make a difference. I do need ZWrite Off, there is no "TransparentCutout" Queue

    "ForwardAdd is supposed to render with One One - it's an additive pass that goes on top of ForwardBase."
    Yes I understand what it's supposed to do - but this is a problem for me - you can see the mesh has mutliple layers since it's used for animations, but it should be displaying as if it is 2d. The multiple layers of lighting are adding up and giving the meshes a transparent effect.

    I have confirmed with pragma debug that changing ForwardAdd blending does actually render correctly, but it is only affect by one light source if I copy the pragma debug shader code into a new shader. How can that be when pragma debug should be showing the exact generated code?
     
  4. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Yes, but the mesh has ZWrite Off, so it doesn't know where it should cull the ForwardAdd passes to stop it blending on areas it shouldn't (it doesn't know where things. Meaning everything is writing over everything.

    It's sort of amazing it sorts properly even on ForwardBase, to be honest.

    For Queue, sorry, it should be AlphaTest - it's RenderType that's TransparentCutout...
    Code (csharp):
    1. "Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"
     
  5. abeck99

    abeck99

    Joined:
    May 9, 2014
    Posts:
    6
    I changed those things.
    Removing "ZWrite Off" causes that striped line effect when the frag shader keeps changing it's mind about which tri it should be drawing.
    Also I wasn't using alphatest:_Cutoff but since you were talking about the transparent cutout queue I tried it with that and with just alpha. No change - the only thing that makes a difference is removing ZWrite Off.


    Basically I want each tri to calculate it's own lighting, only showing the most recently drawn tris from the mesh. I'm not worried about optimizing right now.
     
  6. abeck99

    abeck99

    Joined:
    May 9, 2014
    Posts:
    6
    Ok, I learned more about ForwardAdd behaviour, and you're absolutely right about ZWrite - my problem is that everyone is on the same Z, so I just added enough z offset to force the draw order.

    Thanks for you help!
     
  7. abeck99

    abeck99

    Joined:
    May 9, 2014
    Posts:
    6
    I spoke too soon, there is still one major problem, though it is very close!


    If I use alphatest:_Cutoff I'm getting a black border:
    ShaderWithCutoff.PNG
    which of course I can remove by adjusting the cutoff but the hard edges are really ugly.
    Using alpha instead of alphatest on the surface shader makes smooth edges:
    ShaderWithFullAlpha.PNG

    This really is my original problem, and the alphatest is covering over the problem by forcing no transparency, but somehow transparency is being added when it shouldn't.
     

    Attached Files:

  8. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Then your texture isn't properly made for being cutout - if you've got black outlines it's because your texture has them.

    You'll need to remove those and pad around the RGB channel with the same colour, but leave the alpha alone so it cuts out properly.
     
  9. abeck99

    abeck99

    Joined:
    May 9, 2014
    Posts:
    6
    Ok I fixed the padding issue.

    Last question: Implicit in what you're saying is that this is only possible with cut off? (at least in one draw call)

    Thanks again for your help with my noobishness
     
  10. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Every normal mapped light requires another pass, which is another draw call. In order to render your multi-layer character with more than one light and still using alpha blending, you would need to interleave the two passes. Unfortunately this isn't possible without drastically increasing your draw calls.

    Another approach you could use is to write a custom shader that supported more than one per-pixel light in a single pass. This would mean bypassing Unity's lighting pipeline, or making some assumptions about the lighting information that gets passed to your shader.