Search Unity

Surface Shader using BlendOp Max to blend lighting gets turned transparent in areas

Discussion in 'Shaders' started by Maldruzard, Aug 19, 2021.

  1. Maldruzard

    Maldruzard

    Joined:
    Oct 26, 2014
    Posts:
    17
    Hi there!

    I'm using a pretty simple Toon surface shader that has a custom lighting function in order to get stepped lighting. In order to stop overlapping lights from creating visible overlaps and increasing intensity, I wanted them to blend together instead (or take the brightest light) so I added the 'BlendOp Max' operator to the shader.

    This had the perfect effect on the lighting, which now blends together seamlessly from multiple lights, but now any object with this shader attached is strangely transparent when in front of another object with the same shader, with the lighter parts of the object behind bleeding through the object in front's darker areas.

    I assume this is something to do with the BlendOp Max operator seeing the pixels from the 'behind' object are brighter and showing them instead of the front object, even though none of the objects are explicitly transparent and are in the geometry queue.

    Switching my camera to deferred fixes this, strangely, but this is not a good solution as the camera must be in forward rendering mode for other reasons.

    Am I using the BlendOp Max operator incorrectly? Or does it not play nice with custom lighting functions?

    This is in the built-in pipeline and forward rendering path. Please ask if there's any other info that might help.

    Cheers for any help y'all can provide <3
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    The problem is you only want that
    BlendOp Max
    on the
    "ForwardAdd"
    pass, which Unity uses when rendering multiple lights. You don't want it on the
    "ForwardBase"
    pass, but you don't get that level of granularity with Surface Shaders. You'd need to modify the generated shader code from a Surface Shader to only add the blend mode to the one pass.
     
    Maldruzard likes this.
  3. Maldruzard

    Maldruzard

    Joined:
    Oct 26, 2014
    Posts:
    17
    Riiiiight! That makes a lot of sense, thank you.

    Is there any way to alter this generated shader code 'permanently'? or will I have to make this change every time I change the shader and it is (presumably) re-generated?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    When you click on the button to generate the shader code in the inspector, it pops up a new file that you would need to either copy over your existing shader, or save as a new shader. After that it's "permanent", though if you want to make modifications to the original surface shader you would need to do it again otherwise obviously the copy won't know that you made the change. It's just another shader at that point that is the full output of the surface shader at the time you clicked the button.
     
    Maldruzard likes this.
  5. Maldruzard

    Maldruzard

    Joined:
    Oct 26, 2014
    Posts:
    17
    Thanks so much! Worked like a charm. I'm sad that this requires a workaround like this so it'll make editing the original shader a little harder, kind of annoying surface shaders dont support manually controlling passes but I suppose that's the price we pay for them.

    Cheers again for the help, you're a lifesaver <3
     
  6. Davidjsap

    Davidjsap

    Joined:
    Aug 17, 2017
    Posts:
    11
    Would you mind posting your resulting shader code @JuiceMaster2D ? I've been struggling with this.

    Thanks!
     
  7. Maldruzard

    Maldruzard

    Joined:
    Oct 26, 2014
    Posts:
    17
    Hi @Davidjsap - I'm so sorry I didn't see your reply sooner, I didn't get an email!

    Unfortunately because the 'solution' was to alter the generated code from my toon shader, its not an obvious bit of code to write, but I'll post the steps because that should make more sense:

    1. Don't add the 'BlendOp Max' operator to your regular shader. Instead, keep the regular blend mode in there (
    Blend SrcAlpha OneMinusSrcAlpha
    in my case).
    2. Select the shader in the Unity inspector, and you should see a button there that says 'Show generated code'. Click this, and it should open up a huge gross shader with generated code that was spawned from your original shader.
    3. In this shader, Ctrl+F to find the 'Forward Add' pass, it should be something like (but might be slightly different):

    Code (CSharp):
    1. // ---- forward rendering additive lights pass:
    2.     Pass {
    3.         Name "FORWARD"
    4.         Tags { "LightMode" = "ForwardAdd" }
    5.         ZWrite Off Blend SrcAlpha OneMinusSrcAlpha
    4. Change the blend operator on that line from
    Blend SrcAlpha OneMinusSrcAlpha
    to

    BlendOp Max
    .
    5. Copy and paste all of the shader code inside this modified generated shader file into a new shader file, and name it something different than your original shader (in my case, I just whacked 'Final' on the end of the shader name haha) and change its navigation address at the top too so it doesn't conflict with your original shader.
    6. Apply this new shader to whatever materials you want to have the desired effect!

    This method has the downside that if you want to modify the shader in the future, you have to modify the original non-blendop-max shader, then redo these steps again and overrwrite the code in the 'Final' shader every time, but it does give me the desired effect of only applying the BlendOp Max on the lighting pass, so I can't complain!

    Let me know if that helps or if you need more help, sorry again for not seeing your message for so long!