basic lambert shader does not work

Hi,I am learning graphics pipeline and mostly shading part.I did some code on cgfx within maya and it worked.But when I just plainly copy-pasted into unity,it was horrible.Looks like I am feeding my baby really hot water:rage::(.So I did modification on many parts.Basic single colored shader worked.Then I approached towards light information for building a lambert shader.Then all went really crazy:(.You can look at the code below:

Shader "kaiyum/lambert" 
{   
   Properties {
    _shadercolor ("supersimple color", Color) = (1,1,1,1)
    _ObjectSpaceLightPos("Some Vector", Vector) = (0,0,0,0) //this will give me light source position vector,isn't it?
     }
	SubShader 
	{
		Pass 
		{
		CGPROGRAM
		#pragma vertex vert
		//on the above line we said that there will be a vertex program called vert
		#pragma fragment frag
        //on the above line we said that there will be a fragment/pixel program called frag 
        #include "UnityCG.cginc"
		float4 _shadercolor;
		float4 _ObjectSpaceLightPos; //position vector of light source
		struct vi 
		{
			float4 position : POSITION;  //we will output only position and normal from vertex shader
			float4 normal : NORMAL;
		};
 
		vi vert(appdata_base v)
		{ 
		  vi a;
		  a.position = mul (UNITY_MATRIX_MVP, v.vertex); //we do a transformation here.verex position gets transform into world space
		  a.normal = mul(UNITY_MATRIX_IT_MV,v.normal).xyz; //similar transformation but this does on normal.Wait a min,why this ".xyz" thing?
		  return a;
		}		 
 
		struct pi
		{              //we will output color and worldnormal(I know we need color but why outputing worldnormal?) from pixel shader
			float4 color : COLOR;
		};
		
		pi frag(vi i,appdata_base v) //we need data from "appdata_base" struct and "vi" struct
		{
		    float3 lightdirWRTvertes=((v.position)-(_ObjectSpaceLightPos)); //This will give me the vector from light source to vertex position
		    float3 worldnormal = normalize(i.normal);//I think this line normalize the normal on a vector but again I do not surely know.
		    float3 lightdir = normalize(lightdirWRTvertes);
		    float3 lambert = saturate(dot(lightdir,worldnormal));
			pi b;
			b.color.rgb =lambert*_shadercolor;
			b.color.a=1;//a for transperency or alpha.
			return b;
		} 
		ENDCG
		}
	}
}

By doing that error pops up.error image is below

That’s not how shaders in unity are written.

Lets keep Unity’s conventions first - call the struct v2f (vertex to fragment).

struct v2f {
	float4  pos : SV_POSITION;
	half2	   uv_MainTex : TEXCOORD0;
	...
};

Your vertex program looks ok, but fragment is all messed up. Basically you cannot access data from “appdata_base” in a fragment program. You should write whatever you need in vertex program and add it to v2f struct.
Here’s how your fragment program should look like:

fixed4 frag (v2f i) : COLOR

And also, you don’t need colors to be in float4, fixed4 is more than enough. For vectors half3 is more than enough.

Here’s a working shader:

    Shader "kaiyum/lambert"
    {  
       Properties {
        _shadercolor ("supersimple color", Color) = (1,1,1,1)
         }
        SubShader
        {
            Pass
            {
            CGPROGRAM
            #pragma vertex vert
            //on the above line we said that there will be a vertex program called vert
            #pragma fragment frag
            //on the above line we said that there will be a fragment/pixel program called frag
            #include "UnityCG.cginc"
            float4 _shadercolor;
            
            struct v2f
            {
                float4 position : POSITION;  //we will output only position and normal from vertex shader
                half3 normal;
                
                half3 lightDir: TEXCOORD0;	//Semantics are neccessary, use whatever texcoord you have available.
            };
     
            v2f vert(appdata_base v)
            {
              v2f o;
              o.position = mul (UNITY_MATRIX_MVP, v.vertex); //we do a transformation here.verex position gets transform into world space
              o.normal = normalize( mul( _Object2World, float4(v.normal, 0) ) ); //similar transformation but this does on normal.Wait a min,why this ".xyz" thing?
              o.lightDir = normalize(WorldSpaceLightDir( v.vertex ));
              
              return o;
            }
           
            fixed4 frag(v2f i) : COLOR
            {
                float3 lambert = saturate( dot(i.lightDir, i.normal ) );
                
                fixed4 outColor;
                
                outColor.rgb = lambert * _shadercolor;
                outColor.a = 1.0;//a for transperency or alpha.
                return outColor;
            }
            ENDCG
            }
        }
    }

Also, why not use a surface shader?

@ ilya_ca,Thank you very much.The shader worked.I have some questions.

  1. WorldSpaceLightDir(v.vertex) this is unity specific method that automatically generates the vector from light source to vertex in world-space?Previously I had to calculate it manually by subtracting the light source’s position vector from vertex position vector,isn’t it? And this “WorldSpaceLightDir” refers to which light source? And why worldspace? should we not use eyespace?

2.Why do we use textcoord0 semantics for lightdir? Is it because we need to add light color information into uv texture coordinate of mesh?what is half3? what is the advantage of half3?

3.Fragment program can only accept data output from vertex shader? Can vertex shader accept multiple data input like this?
v2f vert(appdata_base v,extrastruct ext)

4.Our vertex program run on every vertex,isn’t it?Can we do formal programming on vertex shader or fragment shader like looping,switch,break,recursion,condition etc?
5.One thing I need to do.Interaction of shader program with gameobject.Suppose I need to change value of something within shader based on something happened on gameworld.Lets clear a bit.I define my light source as vector at shader properties.I do all the things within shader.Now my light source position vector will be “transform” of a gameobject called “dirlight”.If my gameobject change xyz coordinate within game,so does the coordinate of light source.And value of lambert also change at runtime.How can I do it?

As I am learning shader so I did not use surface shader.I need to go to deep into shader.I hope you guys help me on this.

WorldSpaceLightDir is simply a nice function that’s defined in “UnityCG.cginc”. Take a look at that file it has many nice macros and functions.
WorldSpaceLightDir refers to the current light source in forward rendering (if you have additional shader passes, it will return you the value for a corresponding light source).
It doesn’t matter what space we use as long as all of the calculations are being done in the same space.

Take a look here: http://docs.unity3d.com/Documentation/Components/SL-VertexProgramInputs.html If it’s not vertex, color, normal, tangent or normal we have to use texcoord[something] as semantics.

I don’t think so.

Yes, looping/branching should work as long as the GPU supports it. Usually the loops are unrolled by the compiler(so you might be limited by the amount of iterations). With loops amount of instructions grow, so you should be using

pragma target 3.0

If I understand you correctly, you don’t need to manually update the light’s position within the shader - Unity will do this automatically. You may also provide custom input to a shader from a script like this:

Shader "Custom/Some Shader" {

Properties {
	_myVariable("myVariable", Float) = .0
}
SubShader
{
      Pass
      {
      CGPROGRAM
            half _myVariable;
      ENDCG
      }
}
}

And you change it from a script like this:

gameObject.sharedMaterial.SetFloat("_myVariable", value);

By the way, you can find some really good tutorials on shaders over here: http://en.wikibooks.org/wiki/Cg_Programming/Unity

wow! wikibooks is great for shader learning!!Thanks ilya_ca.

while learning diffuse reflection,I have found some words I do not understand.Rendering Path,forward rendering.What are those? Anybody plz explain.

You just stick with forward rendering for now (it’s easier to write shaders for).
Here’s a link: http://docs.unity3d.com/Documentation/Manual/RenderingPaths.html

Basically deferred lighting path will be much faster for a large amount of lights. In fact I think it supports almost unlimited amount of real-time lights.
However if you have only one or two lights, forward rendering should be enough for you.