Search Unity

SurfaceShader specular blocky.

Discussion in 'Shaders' started by Avol, Jan 2, 2018.

  1. Avol

    Avol

    Joined:
    May 27, 2016
    Posts:
    95
    Why does the default specular from surface lightning examples produce blocky specular?
    Is some vector not being interpolated per fragment but instead per vertex? or is there a lack of precision on some vector? Could not find any docu on this. Ref image shows left custom surface shader and right standard ggx.
     

    Attached Files:

    • refd.png
      refd.png
      File size:
      165.1 KB
      Views:
      768
  2. Avol

    Avol

    Joined:
    May 27, 2016
    Posts:
    95

    Code (CSharp):
    1. SubShader
    2.     {
    3.  
    4.         Tags { "RenderType"="Opaque" }
    5.         LOD 200
    6.        
    7.         CGPROGRAM
    8.         #pragma surface surf SimpleSpecular
    9.  
    10.         half4 LightingSimpleSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
    11.             half3 h = normalize (lightDir + viewDir);
    12.  
    13.             half diff = max (0, dot (s.Normal, lightDir));
    14.  
    15.             float nh = max (0, dot (s.Normal, h));
    16.             float spec = pow (nh, 48.0);
    17.  
    18.             half4 c;
    19.             c.rgb = ( _LightColor0.rgb * spec) * atten;
    20.             c.a = s.Alpha;
    21.             return c;
    22.         }
    23.  
    24.         struct Input
    25.         {
    26.             float2 uv_MainTex;
    27.         };
    28.    
    29.         sampler2D _MainTex;
    30.    
    31.         void surf (Input IN, inout SurfaceOutput o)
    32.         {
    33.             o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    34.         }
    35.         ENDCG
    36.  
    37.     }
    38.     FallBack "Diffuse"
     
  3. Owers

    Owers

    Joined:
    Jul 7, 2012
    Posts:
    39
    Try normalising your normals in the fragment shader. In your lighting function, before the lighting is calculated, add this line:

    Code (csharp):
    1. s.Normal = normalize(s.Normal);
    s.Normal contains your per-pixel world-space normals, which is essential for lighting. Normalising them will help "smooth out" the interpolation you get from the vertex shader, which is why it looks so blocky.

    Keep in mind, if you're using Unity 2017.2+ and a tangent-space normal map (o.Normal in your surf function), all surface shaders will automatically normalise your world-space normals in the fragment shader, so you shouldn't need to do this in those circumstances.
     
    bgolus likes this.
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Did they finally fix that in 2017.2?
     
  5. Owers

    Owers

    Joined:
    Jul 7, 2012
    Posts:
    39
    Yes, thankfully. Surface shaders that write to o.Normal will now generate this:
    Code (csharp):
    1. fixed3 worldN;
    2. worldN.x = dot(IN.tSpace0.xyz, o.Normal);
    3. worldN.y = dot(IN.tSpace1.xyz, o.Normal);
    4. worldN.z = dot(IN.tSpace2.xyz, o.Normal);
    5. worldN = normalize(worldN);
    6. o.Normal = worldN;
    However, this has created a new bug, because surface shaders that use the Standard lighting model are now normalising their world-space normals twice per-pixel, once in the surface shader above, and again in the Standard lighting function found in UnityPBSLighting.cginc. Not exactly great for performance.
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    The shader compiler will likely fix that so it only runs once.
     
  7. Avol

    Avol

    Joined:
    May 27, 2016
    Posts:
    95
    Cool thanks, not sure how I didn't try that :)