Search Unity

How do I remove lighting from the backface of a mesh?

Discussion in 'Shaders' started by strich, Jan 22, 2016.

  1. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    374
    I'm writing a shader to cut away parts of a mesh, which works great. I also need to render the backface of the mesh in an unlit solid color so it looks like a solid cut. I have also been able to successfully do this, all except the backface lighting - I cannot figure out how to disable it.

    Current state of the shader:
    upload_2016-1-22_16-48-1.png


    Shader code:
    Code (CSharp):
    1. Shader "Custom/Cutaway Test2" {
    2.     Properties {
    3.         _Color ("Color", Color) = (1,1,1,1)
    4.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    5.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    6.         _Metallic ("Metallic", Range(0,1)) = 0.0
    7.         _InternalColor("Internal Color", Color) = (1,1,1,1)
    8.         _CutAwayOffset("Cut Away Offset", Range(0,1)) = 0.5
    9.     }
    10.     SubShader {
    11.         Tags { "RenderType"="Opaque" }
    12.         LOD 200
    13.        
    14.         // Back side first
    15.         Cull Front
    16.         Lighting Off
    17.         CGPROGRAM      
    18.  
    19.         #pragma surface surf Standard
    20.  
    21.         sampler2D _MainTex;
    22.         half _Glossiness;
    23.         half _Metallic;
    24.         half _CutAwayOffset;
    25.         fixed4 _Color;
    26.         fixed4 _InternalColor;
    27.  
    28.         struct Input {
    29.             float2 uv_MainTex;
    30.             float3 worldPos;
    31.         };      
    32.  
    33.         // Flip normal for back faces
    34.         void vert(inout appdata_full v) {
    35.             v.normal *= -1;
    36.         }
    37.  
    38.         void surf (Input IN, inout SurfaceOutputStandard o) {
    39.             clip((-IN.worldPos.y + _CutAwayOffset));
    40.             o.Albedo = _InternalColor;
    41.         }
    42.         ENDCG
    43.  
    44.         // Now render front faces
    45.         Cull Back
    46.         Lighting On
    47.         CGPROGRAM
    48.         // Physically based Standard lighting model, and enable shadows on all light types
    49.         #pragma surface surf Standard fullforwardshadows
    50.         // Use shader model 3.0 target, to get nicer looking lighting
    51.         #pragma target 3.0
    52.  
    53.         sampler2D _MainTex;
    54.         half _Glossiness;
    55.         half _Metallic;
    56.         half _CutAwayOffset;
    57.         fixed4 _Color;
    58.         fixed4 _InternalColor;
    59.  
    60.         struct Input {
    61.             float2 uv_MainTex;
    62.             float3 worldPos;
    63.         };
    64.        
    65.         void surf(Input IN, inout SurfaceOutputStandard o) {
    66.             clip((-IN.worldPos.y + _CutAwayOffset));
    67.  
    68.             // Albedo comes from a texture tinted by color
    69.             fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    70.             o.Albedo = c.rgb;
    71.             // Metallic and smoothness come from slider variables
    72.             o.Metallic = _Metallic;
    73.             o.Smoothness = _Glossiness;
    74.             o.Alpha = c.a;
    75.         }
    76.         ENDCG
    77.     }
    78.     FallBack "Diffuse"
    79. }
    80.  
     
  2. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    You can create custom lighting function for back faces. Something like this:
    Code (CSharp):
    1. #pragma surface surf NoLight
    2. ...
    3. half4 LightingNoLight(SurfaceOutput s, half3 lightDir, half atten)
    4. {
    5.     return _InternalColor;
    6. }
    7. ...
     
  3. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    374
    Yeah I tried this and others like it - It just makes the backface transparent.
     
  4. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    I see, you're using deferred renderer. Unless you want to switch to forward rendering, you could just tag back faces as transparent (Queue=Transparent). That will force only back faces to forward rendering and it would work.
     
  5. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    374
    I confirmed that indeed it does work properly in forward. But yes I do require deferred. Added the above to my tags didn't change anything sadly.
     
  6. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    976
    Code (CSharp):
    1. Shader "Custom/Cutaway Test2" {
    2.     Properties {
    3.         _Color ("Color", Color) = (1,1,1,1)
    4.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    5.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    6.         _Metallic ("Metallic", Range(0,1)) = 0.0
    7.         _InternalColor("Internal Color", Color) = (1,1,1,1)
    8.         _CutAwayOffset("Cut Away Offset", Range(0,1)) = 0.5
    9.     }
    10.     SubShader {
    11.         Tags { "RenderType"="Opaque" }
    12.         LOD 200
    13.      
    14.         // Back side first
    15.         Cull Front
    16.         Lighting Off
    17.         CGPROGRAM    
    18.         #pragma surface surf None
    19.         sampler2D _MainTex;
    20.         half _Glossiness;
    21.         half _Metallic;
    22.         half _CutAwayOffset;
    23.         fixed4 _Color;
    24.         fixed4 _InternalColor;
    25.         struct Input {
    26.             float2 uv_MainTex;
    27.             float3 worldPos;
    28.         };    
    29.         // Flip normal for back faces
    30.         void vert(inout appdata_full v) {
    31.             v.normal *= -1;
    32.         }
    33.        
    34.                
    35.         half4 LightingNone(SurfaceOutput s, half3 lightDir, half atten)
    36.         {
    37.             return half4(s.Albedo, 1);
    38.         }
    39.         void surf (Input IN, inout SurfaceOutput o) {
    40.             clip((-IN.worldPos.y + _CutAwayOffset));
    41.             o.Albedo = _InternalColor;
    42.         }
    43.         ENDCG
    44.         // Now render front faces
    45.         Cull Back
    46.         Lighting On
    47.         CGPROGRAM
    48.         // Physically based Standard lighting model, and enable shadows on all light types
    49.         #pragma surface surf Standard fullforwardshadows
    50.         // Use shader model 3.0 target, to get nicer looking lighting
    51.         #pragma target 3.0
    52.         sampler2D _MainTex;
    53.         half _Glossiness;
    54.         half _Metallic;
    55.         half _CutAwayOffset;
    56.         fixed4 _Color;
    57.         fixed4 _InternalColor;
    58.         struct Input {
    59.             float2 uv_MainTex;
    60.             float3 worldPos;
    61.         };
    62.  
    63.      
    64.         void surf(Input IN, inout SurfaceOutputStandard o) {
    65.             clip((-IN.worldPos.y + _CutAwayOffset));
    66.             // Albedo comes from a texture tinted by color
    67.             fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    68.             o.Albedo = c.rgb;
    69.             // Metallic and smoothness come from slider variables
    70.             o.Metallic = _Metallic;
    71.             o.Smoothness = _Glossiness;
    72.             o.Alpha = c.a;
    73.         }
    74.         ENDCG
    75.     }
    76.     FallBack "Diffuse"
    77. }
     
  7. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    374
    @SunnySunshine did you change anything other than the lighting model? Because that still doesn't work in Deferred mode.
     
  8. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    Transparent object are rendered in forward. So, you just have to somehow force it to be rendered as transparent. Try "RenderType"="Transparent" and transparent queue. Use Frame Debugger to verify if it is rendered among transparent geometry.
     
  9. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    976
    Oh rite. I haven't tried it, just my idea how it could be solved. Maybe it doesn't work in deferred.
     
  10. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    374
    mmm. It not working in Deferred would be quite unfortunate. I don't understand why not - We're really only trying to skip a step.
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Don't use Albedo, use Emission if you don't want lighting to effect it. Also you can use the VFACE semantic for facing rather than doing this in two passes. Also this means it'll work properly in deferred as using a different lighting model than Standard with deferred can cause some weirdness.

    Code (ShaderLab):
    1. Shader "Custom/Cutaway Test2" {
    2.     Properties {
    3.         _Color ("Color", Color) = (1,1,1,1)
    4.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    5.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    6.         _Metallic ("Metallic", Range(0,1)) = 0.0
    7.         _InternalColor("Internal Color", Color) = (1,1,1,1)
    8.         _CutAwayOffset("Cut Away Offset", Range(0,1)) = 0.5
    9.     }
    10.     SubShader {
    11.         Tags { "RenderType"="Opaque" }
    12.         LOD 200
    13.         Cull Off
    14.  
    15.         CGPROGRAM
    16.         // Physically based Standard lighting model, and enable shadows on all light types
    17.         #pragma surface surf Standard fullforwardshadows
    18.         // Use shader model 3.0 target, to get nicer looking lighting
    19.         #pragma target 3.0
    20.         sampler2D _MainTex;
    21.         half _Glossiness;
    22.         half _Metallic;
    23.         half _CutAwayOffset;
    24.         fixed4 _Color;
    25.         fixed4 _InternalColor;
    26.         struct Input {
    27.             float2 uv_MainTex;
    28.             float3 worldPos;
    29.             fixed facing : VFACE;
    30.         };
    31.      
    32.         void surf(Input IN, inout SurfaceOutputStandard o) {
    33.             clip((-IN.worldPos.y + _CutAwayOffset));
    34.  
    35.             if(IN.facing > 0) // front face
    36.             {
    37.                 // Albedo comes from a texture tinted by color
    38.                 fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    39.                 o.Albedo = c.rgb;
    40.                 // Metallic and smoothness come from slider variables
    41.                 o.Metallic = _Metallic;
    42.                 o.Smoothness = _Glossiness;
    43.                 o.Alpha = c.a;
    44.             }
    45.             else // backface
    46.             {
    47.                 o.Emission = _InternalColor;
    48.             }
    49.         }
    50.         ENDCG
    51.     }
    52.     FallBack "Diffuse"
    53. }
    54.  
     
    Michal_ and strich like this.
  12. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    In your first pass (Cull Front), dont use a surface shader and just use a vert/frag pass with no lighting involved.
     
  13. Gohan4199

    Gohan4199

    Joined:
    Mar 17, 2023
    Posts:
    6
    Hi I know this is a very old thread but is there a way of doing this in HDRP - Making the backface render in a solid colour?
     
  14. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
  15. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    One issue you'll run into here is "unlit" objects in HDRP are very difficult to achieve due to the fact the HDRP uses real world brightness values for lighting. To get an emissive color that doesn't just appear black, it needs to be multiplied by something like 100000. The HDRP has a special Unlit shader that works specifically for the HDRP which might be a better option to use: one regular lit shader for front faces, one unlit shader for the back faces.
     
    Gohan4199 likes this.
  16. Gohan4199

    Gohan4199

    Joined:
    Mar 17, 2023
    Posts:
    6
    Thank you for the detailed insight!

    Yes I've multiplied the emissive color by 10,000 and now it looks exactly how I wanted it to! (I'm matching the section backface emissive color with the front face color) - see image below! However, when the camera is in specific angles you can start to see the backfaces separately again.

    You've mentioned having one shader for front face (Lit) and one for the backfaces (Unlit)

    Would I essentially use 1 Lit material to show only front faces and 1 Unlit material to show only back faces, then add both of these materials onto the mesh renderer? Or is there a way to using multiple shader passes?

    Thanks for all the help!

    upload_2023-3-18_11-53-17.png
     
  17. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    You can either apply both materials to one mesh, if the mesh already has multiple materials, you’ll have to use two meshes.