Search Unity

additive light pass/transparent material issue

Discussion in 'Shaders' started by rsamuel, Aug 16, 2017.

  1. rsamuel

    rsamuel

    Joined:
    Aug 14, 2017
    Posts:
    6
    Hey everyone, I made a post in General support that didn't get much attention, so I figured I would post here before committing to a work-around.

    The problem is basically this: additive light passes seem to incorrectly add their light to any transparent object within a merged polygon group, even if they are completely obscured. This happens on both the standard shader with transparency/fade enabled, and even transparent legacy shaders.

    This makes lighting a character with traditional layered polygon hair with more than one light produce ugly lighting artifacts. Are there any shading gurus who would know if its possible to tweak the standard shader in some way to alleviate this?

    It seemed strange that I couldn't really find anyone else who had this issue. The only thing I found was one thread from 2014 that seems to describe the same issue https://forum.unity3d.com/threads/t...s-additive-when-triangles-are-stacked.253977/. There never really seemed to be a solution found in that thread, the closest being marking the additional lights as unimportant so they switch to vertex lights, which unfortunately make the lights quite a bit lower quality.

    Here's a couple pictures showing the problem.





    If there isn't any easy way to tweak the standard shader ill accept that. But it seemed like such a standard thing to me to be not working correctly given how common polygon hair or any merged/transparent polygon group would be in games.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Working as expected for the way Unity handles additive lights.

    Unity supports multiple lights by rendering an object once with the directional light and ambient lighting, then it renders the entire* object again with just the additional light. The benefit of this technique is they can support an arbitrary number of lights with no additional overhead for objects that don't support the maximal number of lights even on platforms that don't support or are inefficient at handling dynamic branching in shaders.
    * Clipped to the screen space rectangle the light's bounds cover.

    The down side is it's a lot slower to render multiple lights than it could be, and lighting on transparencies is weird.

    Basically Unity's current forward renderer is limited by still having to support OpenGL ES 2.0 and DX9 devices, and is conceptually the same as the original renderer they wrote to run on OpenGL ES 1.0.

    The problem transparent objects in real time rendering don't (generally) render to the depth buffer. The depth buffer can hold a single depth per-pixel that is effectively the closest pixel so far rendered. If something tries to render that's further away, it can just skip rendering that preventing objects from overlapping regardless of the order they render. Transparencies will get rejected the same way, but since it's totally valid to see multiple transparent "things" at the same pixel they can't rely on the depth buffer to sort. The result is transparency rendering uses what's called the painter's algorithm which each object just renders entirely over what's been rendered before.

    The unmerged case works because Unity is rendering like this:
    1. Object 1 - base pass
    2. Object 1 - additive light pass
    3. Object 2 - base pass
    4. Object 2 - additive light pass
    5. Object 3 - base pass
    6. Object 3 - additive light pass
    As long as the objects are rendering furthest to closest this all works. Because the merged case is rendering all of the planes at once for both the base and additive passes, and they're transparent, the additive light pass just renders on top.

    Solutions / workarounds:
    • Use a two pass shader that renders the fully opaque areas as opaque and only the transparent areas as transparent. I posted an example shader on the forums here. The double-lighting issue remains to an extent on areas of overlapping partial opacity.
    • Use a custom shader that uses vertex lights, but the PBR Standard shading model. Unity will pass 8 lights to the base pass which could be used to calculate multiple lights in a single pass. Some hair shader assets might already do this?
    • If you're using MSAA you could use an Alpha to Coverage shader. Or you could use Hashed Alpha Testing, especially if you're using FXAA or TAA.
    • Completely replace Unity's entire lighting system with your own custom one... ala Valve's The Lab Renderer. Or wait for Unity to release their new HD Render Loop as part of the Scriptable Render Loop system they're working on which should be released sometime in the next year or two. (The length of Unity years may or may not conform to standard earth years.)
     
    AcidArrow likes this.
  3. rsamuel

    rsamuel

    Joined:
    Aug 14, 2017
    Posts:
    6
    Thanks for the in-depth response bgolus, I thought that might be the case, I'll check out your alternative workarounds.
     
  4. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Also nearly most games forgo transparent hair for dithered transparency and alpha cut out, so it's a more generalized problem than unity.

    edit:
     
    Last edited: Aug 21, 2017
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Crytek did a hack for the Crysis games where the hair was rendered with dithered alpha test, and then they added a small amount of faux "motion blur" in the orientation of the hair to smooth it out.
     
    IgorAherne and neoshaman like this.
  6. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Temporal anti aliasing is also used to smooth stuff, all those techniques are beyond my skill level though lol