Search Unity

Surface Shader lighting inconsistency with the standard specular

Discussion in 'General Graphics' started by GameArt, Apr 20, 2016.

  1. GameArt

    GameArt

    Joined:
    Feb 24, 2013
    Posts:
    78
    Hello,

    I'm using Unity 5.3.3p3 and I'm noticing an issue with my specular surface shader based materials not shading the same as the default/built in shaders.

    Here I just put a normal map on the standard specular (left) and on a basic surface shader (right). The surface shader has less pronounced normal mapping.

    This issue only occurs when I use a surface shader and don't build directional specular lightmaps for it (dynamically loading it in). When I build lightmaps the issue goes away.

    The base gbuffer values are exactly same same, but the lighting buffer computes different lighting.


    When I go into the frame debugger and look at the lighting buffer (RT3) I see the surface shader does not consider the normal map



    I note that the surface shader has a different compiler tag
    "UNITY_HDR_ON" vs "UNITY_HDR_ON_NORMALMAP"

    but I can't find any information on how to force the surface shader to use this normalmap tag. Has anyone encountered this issue before? Does anyone know if I'm just missing a simple tag or step to make this work?

    An example surface shader I use to reproduce this issue:
    Am I missing some macro symbol to force the lighting to match?
    Code (CSharp):
    1. Shader "surface_shader_lighting_test"
    2. {
    3.     Properties
    4.     {
    5.         [Normal] _BumpMap("Normal Map", 2D) = "bump" {}
    6.         _BumpScale("Scale", Float) = 1.0
    7.     }
    8.  
    9.     SubShader
    10.     {  
    11.         CGPROGRAM
    12.  
    13.         #pragma surface surface_shader StandardSpecular
    14.  
    15.         sampler2D _BumpMap;
    16.         float _BumpScale;
    17.  
    18.         struct Input
    19.         {
    20.             float2 uv_BumpMap;
    21.             float4 color : COLOR;
    22.             INTERNAL_DATA
    23.         };
    24.  
    25.         void surface_shader(Input IN, inout SurfaceOutputStandardSpecular output)
    26.         {
    27.             float3 normal_sample = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_BumpMap), _BumpScale);
    28.             output.Normal = normal_sample;
    29.             output.Albedo = float3(1, 1, 1);
    30.             output.Emission = float3(0, 0, 0);
    31.             output.Occlusion = 1.0f;
    32.             output.Smoothness = 0.5f;
    33.             output.Specular = float3(0, 0, 0);
    34.         }
    35.        
    36.         ENDCG
    37.     }
    38.    
    39.     FallBack "Standard (Specular setup)"
    40. }
     
  2. GameArt

    GameArt

    Joined:
    Feb 24, 2013
    Posts:
    78
    I probably should have posted this in the shaders section. If a mod sees this please move the thread.
     
  3. smd863

    smd863

    Joined:
    Jan 26, 2014
    Posts:
    293
    It's hard to see, but there is actually a space in there between "UNITY_HDR_ON" and "_NORMALMAP". "_NORMALMAP" is its own keyword that you will need to turn on.

    If you download the built-in shaders, it comes with the "StandardShaderGUI.cs" file which does a lot of behind-the-scenes magic. It sets keywords, blend modes, and also some flags for baking GI which I imagine are important for your shader to bake properly. A few important but easy to overlook details.

    Specifically, in SetMaterialKeywords() function it checks for a normal map or a detail normal map, and sets the keyword if it finds either.

    Code (csharp):
    1.  
    2. SetKeyword (material, "_NORMALMAP", material.GetTexture ("_BumpMap") || material.GetTexture ("_DetailNormalMap"));
    3.  
    You should be able to simply set it from code (built a custom GUI using StandardShaderGUI as a template) or activate "Debug" mode in the inspector and just manually add it to your material.
     
  4. GameArt

    GameArt

    Joined:
    Feb 24, 2013
    Posts:
    78
    Good spot on the space!

    But unfortunately, I have already forced that define. I manually ensured the .mat file for my two test shaders perfectly matched (except for the shader file reference).

    It still does not show up in the frame debuggers list of keywords. It looks like the surface shader is not honouring that setting?

    The only other difference I'm noticing now, is that the surface shader says "pass 2" and the standard shader says "pass 3"

    Evidence that the keywords are being set on my end:


    The two materials perfectly match (except for the shader gui, and shader type value)

    Code (CSharp):
    1. %YAML 1.1
    2. %TAG !u! tag:unity3d.com,2011:
    3. --- !u!21 &2100000
    4. Material:
    5.   serializedVersion: 6
    6.   m_ObjectHideFlags: 0
    7.   m_PrefabParentObject: {fileID: 0}
    8.   m_PrefabInternal: {fileID: 0}
    9.   m_Name: testtest
    10.   m_Shader: {fileID: 4800000, guid: 0764bd00214a57a4faf329df2f8c3512, type: 3}
    11.   m_ShaderKeywords: _NORMALMAP
    12.   m_LightmapFlags: 5
    13.   m_CustomRenderQueue: -1
    14.   stringTagMap: {}
    15. ...
    16. etc
    17.  
     
  5. GameArt

    GameArt

    Joined:
    Feb 24, 2013
    Posts:
    78
    I still haven't been able to find a solution to this. Does anyone know if there is another way to enable internal shader keywords that surface shaders will respect?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    8,138
    You could just hack around the issue and use a 1 pixel normal map in your shaders you want to be able to change on the fly.
     
  7. GameArt

    GameArt

    Joined:
    Feb 24, 2013
    Posts:
    78
    Unfortunately, the material already has a normal map.
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    8,138
    Ah, I see the problem. The different passes and keywords are a bit of a red herring. The standard (or standard specular) shader and the surface shader using standard or standard specular are entirely different shaders and while many of the keywords are analogous there are several that the standard shader uses the surface versions do not and vice versa.

    The thing you're really missing is #pragma target 3.0, which enables the higher quality lighting. Without that the shader is defaulting to per vertex only ambient.
     
    theANMATOR2b and GameArt like this.
  9. GameArt

    GameArt

    Joined:
    Feb 24, 2013
    Posts:
    78
    That was exactly it! Thank you bgolus.
     
  10. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,782
    You are like some kind of Unity trouble shooting savant!
    Just amazing. :cool:
     
unityunity