Lighting Model Inconsistency

I’m new to shaders and trying to learn to make a custom blinn-phong lighting model, with some additional effects like modifying the color. What I’ve got looks correct in editor, but on iOS 6 and 7 the specular blows out and the gloss amount appears to change, giving the effect of looking very plastic.

I’ve bumped all of my setting to the highest possible on build. I wonder, am I doing anything in my shader that’s incompatible with mobile?

Shader "Custom/LightingModelTest" 
{
    Properties 
    {
      _MainTex ("Texture", 2D) = "white" {}
      _Color ("Tint Color", Color) = (1,1,1,1)
      _DiffPower ("Diff Power", Range(0.0,5.0)) = 1.0
      _BumpMap ("Bumpmap", 2D) = "bump" {}
      _SpecMap ("Specular", 2D) = "spec" {}
      _SpecularPower ("Specular Power", Range(0.01,100.0)) = 0.01
      _SpecularGloss ("Specular Gloss", Range(0.01,100.0)) = 0.01
    }

    SubShader 
    {
      Tags { "RenderType" = "Transparent" "Queue" = "Transparent"}
      Pass {
        ZWrite On
        ColorMask 0
    }
      CGPROGRAM
      // Using the custom lighting modle SimpleSpecular defined below
      #pragma surface surf SimpleSpecular alpha

      struct SurfaceOutputColorSpec 
      {
            fixed3 Albedo;
            fixed3 Normal;
            fixed3 Specular;
            fixed3 Emission;
            fixed Alpha;
            fixed SpecPower;
            fixed SpecGloss;
            float DiffPower;
            float4 Color;
      };

      half4 LightingSimpleSpecular (SurfaceOutputColorSpec s, half3 lightDir, half3 viewDir, half atten) 
      {
          // Get half reflection - Blinn-Phong model faster than calculating Phong
          half3 h = normalize (viewDir + lightDir);
          float nh = max (0, dot (s.Normal, h));

          // nh to the power of SpecGloss controls the size of the light glow over the surface
          float spec = pow (nh, s.SpecGloss) * s.Specular * s.SpecPower;

          // Get NdotL angle for diffuse lighting
          half diff = dot (s.Normal, lightDir);

          // Apply diffuse color and lighting, and add specular color on top
          half4 c;
          c.rgb = (s.Albedo * _LightColor0.rgb * diff * s.DiffPower * s.Color.rgb + _LightColor0.rgb * spec) * (atten * 1.5);
          c.a = 1;
          return c;
      }

      struct Input 
      {
          // Pass in uv maps to surf
          float2 uv_MainTex;
          float2 uv_BumpMap;
      };

      sampler2D _MainTex;
      sampler2D _BumpMap;
      sampler2D _SpecMap;
      float _SpecularPower;
      float _SpecularGloss;
      float _DiffPower;
      float4 _Color;

      void surf (Input IN, inout SurfaceOutputColorSpec o) 
      {
          // Pass specular values from inspector into the output struct
            o.SpecPower = _SpecularPower;
            o.SpecGloss = _SpecularGloss;
            o.DiffPower = _DiffPower;
            o.Color = _Color;

          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;

          // Apply the normal map
          o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));

          // Apply the specular map
          o.Specular = tex2D (_SpecMap, IN.uv_MainTex).rgb;
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

You likely have your project set to use Linear color space. Mobile doesn’t (yet) support linear and falls back to gamma space.

Thanks! Unfortunately, looks like I am already using Gamma color space. Interestingly, the effect that I’m seeing looks just like the effect of the gamma vs linear example on the head example here: Unity - Manual: Linear or gamma workflow

One thing I’d like to try is pasting in the source of the standard specular shader’s lighting model, but I don’t see it in the shader source files. Looks like it’s something built in? Where can I otherwise find that?

That’s not a simple thing to do. The Standard shader is several thousand lines of code spread across a dozen files.

If you really want to look at it you can download the shader source here:

No problem - I found this which looks promising: https://discussions.unity.com/t/381328
I’m trying this out to see if the differences between Aras’ example and mine give a clue. Notably I see they’re normalizing the light direction (for dir lights) and the view direction.

However, I have some confusion over where color attribute comes from. Is it just a color property defined somewhere outside of the example. (like _Color in my shader)

Ah! Right!

Unity doesn’t normalize the normal vector in the fragment shader on mobile. This is a common way to optimize on mobile, and is often benign, but Unity also doesn’t normalize it in the vertex shader which most people do. That means scaled meshes (either in editor or on import) will have broken lighting.

I have no idea how to fix this for surface shaders normally, but for your case try normalizing the normal in the custom lighting function.

Update: It turns out the issue was one of putting the spec map calculation in the wrong place. It seems the weirdness I was seeing was essentially another spec over the entire model, not affected by the map.

I changed line 53 above to this:

c.rgb = (s.Albedo * _LightColor0.rgb * diff * _DiffPower * _Color.rgb + spec * diff * s.Specular.rgb) * (atten * 1.5);

For reasons I partially understand, multiplying the spec here at the end did the trick, instead of above where I was multiplying at line 46 into the spec variable calculation.