Understanding Surface Shader Lighting

I have read the documentation on the subject, however I still have some doubts about how does it work.

for example, if I have something as simple as:

Shader "Example/Custom Test" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Custom

      half4 LightingCustom (SurfaceOutput s, half3 lightDir, half atten) {
          return half(0,0,0,0);
      }

      struct Input {
          float2 uv_MainTex;
      };
      sampler2D _MainTex;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      }
      ENDCG
    }
    Fallback "Diffuse"
}

Even if I return <0,0,0,0> in my lightning function, the object does not get totally black, you can still see the texture but just darker, so… how does Unity calculates the lightning here?.. it first executes “surf” and then it blends the result from “custom()”??.. shouldn’t just pass the result from “surf()” to “custom()” and let “custom()” do whatever it would like with it and be its return value the final result?..

or what is the logic behind?..

I guess we should expand the documentation to include a lot of details, but for now, reading Tim’s Kuba’s presentation from Unite 2012 should be helpful: x.com

The presentation linked from the tweet above has some details on how to debug surface shaders (i.e. see what is the actual code being generated), as well as details on how the lighting pipeline works.

In your particular question; the surface lighting function does the bit of “how to calculate illumination of one light”, and that bit always returns black. However, Unity also generates code that looks somewhat like “albedo * ambient” that is always added to the final result. So what you see is most likely ambient lighting; if you set ambient to zero in render settings then all should be black.