Simple bumpmap on Surface Shader and pure Fragment Shader differ

I’m doing a simple bumpmapping using a Surface Shader, and everything works ok… as expected.

however, when doing the exact same bumpmapping in pure Fragment Shader… it behaves oddly… if I render a cube, all faces of that cube get lit when the light is facing at one point, and get darker when it faces the other way (instead of just the faces where the light is pointing at).

so… is there any particular difference between doing this in a Surface Shader:

Shader "Test/SurfaceShaderBump" {
	Properties {
		_Diffuse ("Diffuse",2D) = "white" {}
		_NormalMap ("Normal Map", 2D) = "bump" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
				
		CGPROGRAM
		#pragma surface surf Custom
		
		half4 LightingCustom( SurfaceOutput s, half3 lightDir, half3 viewDir, half atten )
		{
			half pxlAtten = dot( lightDir, s.Normal );
			
			return half4(s.Albedo * pxlAtten,1.0);
		}

		sampler2D _Diffuse;
		sampler2D _NormalMap;

		struct Input {
			float2 uv_Diffuse;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			o.Albedo = tex2D (_Diffuse, IN.uv_Diffuse).rgb;
			o.Normal = UnpackNormal( tex2D(_NormalMap, IN.uv_Diffuse) );
		}
		ENDCG
	} 
}

and doing this in a Fragment Shader:

Shader "Test/FragmentShaderBump" {
	Properties {
		_Diffuse ("Diffuse",2D) = "white" {}
		_NormalMap ("Normal Map", 2D) = "bump" {}
	}
	SubShader {
		Pass{
			Tags { "LightMode" = "ForwardBase" }
		
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
				
			struct vertexInput {
				float4 vertex : POSITION;
				float4 texcoord : TEXCOORD0;
			};
		
			struct vertexOutput {
				float4 pos : SV_POSITION;
				float4 uv : TEXCOORD0;
			};

			vertexOutput vert( vertexInput v )
			{
				vertexOutput o;
				
				o.pos  = mul( UNITY_MATRIX_MVP, v.vertex );
				o.uv   = v.texcoord;
				
				return o;
			}

			sampler2D _Diffuse;
			sampler2D _NormalMap;
			half4 frag( vertexOutput i ) : COLOR
			{
				float3 lightDir = normalize( float3(_WorldSpaceLightPos0 ) );
				half3  Albedo = tex2D(_Diffuse, i.uv.xy ).rgb;
				
				half3 pNormal = UnpackNormal( tex2D(_NormalMap, i.uv.xy ) );
				half pxlAtten = dot( half3(pNormal.rgb), lightDir );
				
				half3 diff = Albedo * pxlAtten;

				return half4( diff, 1 );
			}
			ENDCG
		}
	}
}

to my understanding they should be doing exactly the same thing… however on this last one, all faces of the cube get lit at the same time (when the light is pointing at some point) and all faces get unlit at the same time (when turning it away from that point).

Any ideas of what might I be doing wrong?

Thanks!

Multiple errors, see Cg Programming/Unity/Lighting of Bumpy Surfaces - Wikibooks, open books for an open world

For a start, you’re using a tangent space normal map but you’re not putting anything into tangent space…

For that, in your vertex shader you need to create an object-space-to-tangent-space matrix and then throw the light and view vectors through them. There’s no need to calculate these in the pixel shader unless you really need to have the best possible normal mapped lighting (i.e. you’re using this and you want it pixel perfect)

UnityCG.cginc has a macro for this:
TANGENT_SPACE_ROTATION;

THen the values of light and view vectors you pass through to the pixel shader should be:
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));

It’s a good idea to normalize these values in the pixel shader before using them if you have the instructions free. The interpolated values look a bit ugly.

Thanks Farfarer, thanks Martin,

I’m starting to grasp all this tangent thing, but your posts have been very enlightening for me…

Thanks again!