Search Unity

Double sided custom shader looks strange - Need Help.

Discussion in 'Shaders' started by RumataM, May 23, 2018.

  1. RumataM

    RumataM

    Joined:
    Nov 6, 2016
    Posts:
    15
    I'm very new to Shaders programming, I was trying to create a shader which:

    • Allows to change 2 textures with 2 corresponding normal maps (fade in/fade out)
    • Doublesided (show mesh from both sides of the surface)
    Here is what I end up with:

    Code (CSharp):
    1.      Shader "Custom/TextureBlend" {
    2.          Properties {
    3.              _Color ("Color", Color) = (1,1,1,1)
    4.              _Blend ("Texture Blend", Range(0,1)) = 0.0
    5.              _MainTex ("Albedo (RGB)", 2D) = "white" {}
    6.              _MainTex2 ("Albedo 2 (RGB)", 2D) = "white" {}
    7.              _Glossiness ("Smoothness", Range(0,1)) = 0.5
    8.              _Metallic ("Metallic", Range(0,1)) = 0.0
    9.              _BumpMap ("Bumpmap", 2D) = "bump" {}
    10.              _BumpMap2 ("Bumpmap", 2D) = "bump" {}
    11.          }
    12.          SubShader {
    13.              Tags { "RenderType"="Opaque" }
    14.              LOD 200
    15.               Cull Off
    16.               ZTest LEqual
    17.  
    18.              CGPROGRAM
    19.              // Physically based Standard lighting model, and enable shadows on all light types
    20.              #pragma surface surf Standard fullforwardshadows
    21.      
    22.              // Use shader model 3.0 target, to get nicer looking lighting
    23.              #pragma target 3.0
    24.      
    25.              sampler2D _MainTex;
    26.              sampler2D _MainTex2;
    27.              sampler2D _BumpMap;
    28.              sampler2D _BumpMap2;
    29.      
    30.              struct Input {
    31.                  float2 uv_MainTex;
    32.                  float2 uv_MainTex2;
    33.                  float2 uv_BumpMap;
    34.                  float2 uv_BumpMap2;
    35.              };
    36.      
    37.              half _Blend;
    38.              half _Glossiness;
    39.              half _Metallic;
    40.              fixed4 _Color;
    41.    
    42.              void surf (Input IN, inout SurfaceOutputStandard o) {
    43.                  // Albedo comes from a texture tinted by color
    44.                  fixed4 c = lerp (tex2D (_MainTex, IN.uv_MainTex), tex2D (_MainTex2, IN.uv_MainTex2), _Blend) * _Color;
    45.                  o.Albedo = c.rgb;
    46.                  // Metallic and smoothness come from slider variables
    47.                  o.Metallic = _Metallic;
    48.                  o.Smoothness = _Glossiness;
    49.                  o.Alpha = c.a;
    50.                  fixed4 n = lerp (tex2D (_BumpMap, IN.uv_BumpMap), tex2D (_BumpMap2, IN.uv_BumpMap2), _Blend) * _Color;
    51.                  o.Normal = n.rgb;
    52.              }
    53.              ENDCG
    54.          }
    55.          FallBack "Diffuse"
    56.      }

    It works but for some reason the texture on the surface looks dim (on the other side of the mesh which usually invisible it's even darker), and normal map reflections barely visible.

    Here, on the left you can see the plane with my shader, on the right - standard shader:



    Also the reflection looks really strange, if I will move Metallic slider to the right you will see it:




    I would be super grateful for any help.
    Thank you very much in advance!
     
  2. iamvideep

    iamvideep

    Joined:
    Oct 27, 2017
    Posts:
    118
    I am not sure of the answer my self, but I think it has got something to do with the normals. There should be something more that you need to add to the shader to make it work in both sides as a plane will always have normals in one direction.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    @iamvideep is correct. The problem for lit double sided materials is the vertex normals are aligned to the front face of the tri, so when you turn off culling it's rendering with the normals pointed away from you. The math for lighting and reflections breaks down as there are assumptions made that there will never be more than 90 degrees difference between the view direction and the surface normal.

    The trick is to use VFACE, which is a variable that is passed to fragment shaders (which the surface function is called inside of) to say which side of a tri you're rendering.

    Code (csharp):
    1. struct Input
    2. {
    3.     // usual UV stuff
    4.     fixed facing : VFACE;
    5. }
    6.  
    7. // in surf function
    8. o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
    9. o.Normal.z *= IN.facing;
    One extra note, you really should be using the UnpackNormal() function if you're using Unity's Normal Map texture import type. On mobile it doesn't really matter, but on desktop and consoles it will make a difference. If you are importing normal maps as regular textures, do make sure to set Bypass sRGB on the texture.
     
    iamvideep likes this.