Search Unity

Deferred shader with double-sided translucency, how?

Discussion in 'Shaders' started by callebo_FK, Dec 22, 2017.

  1. callebo_FK

    callebo_FK

    Joined:
    Nov 23, 2017
    Posts:
    30
    I have been working on a foliage shader but am stuck on its lighting. What I'm looking for is double-sided translucency in deferred path. If a quads frontface is lit, its backface should also be lit and vice versa. I have had some success with custom lighting but that means forward rendering which I unfortunately cannot use in this case.

    I have some pictures and examples of what my shader is doing and where its going wrong:
    Code (ShaderLab):
    1. Shader "TwosidedNoFlip"
    2. {
    3.     Properties
    4.     {
    5.         _ColorFront ("Front Color", Color) = (1,0,0,1)
    6.     _ColorBack ("Back Back", Color) = (0,1,0,1)
    7.     }
    8.  
    9.     SubShader
    10.     {
    11.         Tags { "Queue" = "Geometry" "RenderType" = "Opaque" }
    12.      
    13.     Cull Off
    14.         CGPROGRAM
    15.         #pragma surface surf Standard
    16.      #pragma target 3.0
    17.    
    18.         float4 _ColorFront;
    19.         float4 _ColorBack;
    20.      
    21.         struct Input
    22.         {
    23.         fixed facing : VFACE;      
    24.     };
    25.      
    26.         void surf (Input IN, inout SurfaceOutputStandard o)
    27.         {
    28.          
    29.             if (IN.facing < 0.5) //If it is backface
    30.         {
    31.                 o.Albedo = _ColorBack;
    32.         }
    33.         else //If it is frontface
    34.         {
    35.                 o.Albedo = _ColorFront;
    36.         }
    37.         }
    38.         ENDCG
    39.     }
    40. }
    The results is:


    The top part is what I want; both sides to be lit when one of the sides is hit with a light. The problem with this shader is that if the backface is lit, none of the faces receives the lighting.
    The alternative I have come up with is:
    Code (ShaderLab):
    1. Shader "TwosidedYesFlip"
    2. {
    3.     Properties
    4.     {
    5.         _ColorFront ("Front Color", Color) = (1,0,0,1)
    6.     _ColorBack ("Back Back", Color) = (0,1,0,1)
    7.     }
    8.  
    9.     SubShader
    10.     {
    11.         Tags { "Queue" = "Geometry" "RenderType" = "Opaque" }
    12.      
    13.     Cull Off
    14.         CGPROGRAM
    15.         #pragma surface surf Standard
    16.      #pragma target 3.0
    17.    
    18.         float4 _ColorFront;
    19.         float4 _ColorBack;
    20.        
    21.         struct Input
    22.         {
    23.         fixed facing : VFACE;      
    24.     };
    25.      
    26.         void surf (Input IN, inout SurfaceOutputStandard o)
    27.         {
    28.          
    29.             if (IN.facing < 0.5) //If it is backface
    30.         {
    31.                 o.Albedo = _ColorBack;
    32.                 o.Normal *= -1.0; //Flip backfaces normals
    33.         }
    34.         else //If it is frontface
    35.         {
    36.                 o.Albedo = _ColorFront;
    37.         }
    38.         }
    39.         ENDCG
    40.     }
    41. }
    The only part that changed is an added line "o.Normal *= -1.0;" in the surf function. This flips the normals of the backface so they point outward from the backfaces perspective. This results in:

    Now the quad can get lit from both sides, but the light doesn't shine through. It needs to shine through!

    I know wherein the problem lies: The normals of each face point either outward or they both point in just one direction, when a solution for me would be if both faces pointed in both directions simultaneously. To my understanding this isn't possible.

    Is there any other solution to this problem? Can you somehow gather the "negative" light and use that to OneMinus it and use it in the emission? What I mean with negative light:

    Picture from


    Can I make a custom lighting pass but still somehow keep it deferred?

    Any suggestions on where to look is appreciated, thanks
     
    ASMRPL2022ZBI likes this.
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Here's the short version:
    You cannot do translucent lighting with Unity's deferred rendering path since it does not support it. You must use a custom shading model.

    Here's the "long" version:
    You need to use a custom deferred shading model.

    ...

    That's the long version because that needs some explaining, and is more work.

    Fundamentally when it comes to deferred rendering you can only use the shading model(s) the deferred renderer supports. In Unity's case the only shading model it supports is the Standard shading model. No more and no less. However Unity made it relatively easy to replace the built in deferred shading model with what ever model you want by replacing the shader Unity uses to do the deferred shading.

    In the Graphics settings there's an option to supply a custom deferred shader. To support translucent lighting you'll need to replace that shader with one that adds support for translucent lighting, as well as use custom shaders on your objects to enable that translucency.

    To do this means you need to dig pretty deeply into how deferred rendering works in Unity, how Unity's default shading model works, and how to write custom deferred compatible shaders.

    Or you can get an asset on the store that's already done the work for you. Something like Uber Standard Shader Ultra, Alloy, or Lux. The later two are free, but completely replace Unity's lighting model and thus require everything in your project use their custom shaders to work properly with deferred instead of the Standard shader. If you want to just look at the code to try to see what's involved, that's an option. Uber works with the Standard shader and adds support for translucent lighting.
     
  3. CoolSmek

    CoolSmek

    Joined:
    Jun 3, 2014
    Posts:
    7
    I believe I have seen this concept work with deferred shading. I am a bit confused with what you mean by "custom lighting but that means forward rendering" part. Are you referring to what the documentation is describing here: Custom lighting models in Surface Shaders?

    Can you not use the "Lighting<Name>_Deferred" option?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You can use that to modify the ambient lighting to a degree, but the other outputs are what gets written to the gbuffers. The nature of deferred rendering is the shader on the object has no control of the shading model used by real time lights, only the parameters the shading model uses.

    https://docs.unity3d.com/Manual/RenderTech-DeferredShading.html
     
  5. callebo_FK

    callebo_FK

    Joined:
    Nov 23, 2017
    Posts:
    30
    Thanks for the very insightful response!

    Does this mean it does not require a custom deferred shading model?

    I have been looking at the Advanced Foliage Shader (https://www.assetstore.unity3d.com/en/#!/content/68907), and it also mentions Deferred Translucency. I don't remember where I read it but I think this shader also does not require a custom deferred shading model. So it IS possible to do without rewriting the lighting for everything?

    Is my theory of using "negative light" applicable at all in this situation or is that something that can't be accessed due to the nature of deferred rendering?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Both Uber and Advanced Foliage Shader use custom deferred shading model, but they are compatible with Unity's Standard shaders such that they look will exactly the same if translucent lighting is not enabled on the material.

    Your "negative light" idea is almost exactly how translucent lighting was acheived in the Unreal Engine prior to Unreal Engine 4. However, as it's making use of the light direction that by definition would be part of the lighting / shading model ... which would still require a custom deferred shading model.