Shader "Example/Diffuse Simple" 
{
    SubShader 
		{
      Tags { "RenderType" = "Opaque" }

      CGPROGRAM
      #pragma surface surf Lambert

      struct Input 
			{
          float4 color : COLOR;
      };

      void surf (Input IN, inout SurfaceOutput o) 
			{
          o.Albedo = 1; // 1 = (1,1,1,1) = white
      }
      ENDCG
    }
    Fallback "Diffuse"
}

#pragma surface surf Lambert specify the shader is surface and use Lambertian Lighting Model.

SurfaceOutput has several properties to determine the final aspect of a material.

  • fixed3 Albedo: the base color/texture of an object
  • fixed3 Normal
  • fixed3 Emission: how much light this object is generating by itself
  • half Specular: light reflection
  • fixed Alpha

float is supported, but we barely use 32 bit precision, so we use half. 16 bits. fixed spans at least from -2 to 2, 10 bits.

Sampling Textures

Texture maps 3D objects. They typically contain UV and color data. UV is a 2D vector which indicates which point of the texture is mapped to the vertex.

Shader "Example/Diffuse Texture" 
{
    Properties 
		{
      _MainTex ("Texture", 2D) = "white" {}
    }
    
		SubShader 
		{
      Tags { "RenderType" = "Opaque" }

      CGPROGRAM
      #pragma surface surf Lambert
      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"
}

_MainText is a texture. uv_MainTex is the current pixel’s UV data. tex2D returns the RGBA color by giving the pixel’s texture and UV data. UV coordinates are stored only in the vertices. When the shader evaluates a pixel is not a vertex, the function tex2D interpolates the UV coordinates of the three closest vertices.

Snow Shader

Shader "Custom/SurfaceShader"
{
	Properties
	{
		_MainColor("Main Color", Color) = (1., 1., 1., 1.)
		_MainTex("Texture", 2D) = "white" {}
		_Bump ("Bump", 2D) = "bump" {}
		_Snow("Level of snow", Range(1, -1)) = 1
		_SnowColor ("Color of snow", Color) = (1., 1., 1., 1.)
		_SnowDirection("Direction of snow", Vector) = (0, 1, 0)
		_SnowDepth("Depth of snow", Range (0, 0.0001)) = 0
	}

	SubShader
	{
		Tags 
		{
			"RenderType" = "Opaque"
		}
		LOD 200

		CGPROGRAM
		#pragma surface surf Lambert vertex:vert

		sampler2D _MainTex;
		sampler2D _Bump;
		float _Snow;
		float4 _SnowColor;
		float4 _MainColor;
		float4 _SnowDirection;
		float _SnowDepth;

		struct Input
		{
			float2 uv_MainTex;
			float2 uv_Bump;
			float3 worldNormal;INTERNAL_DATA
		};

		void vert (inout appdata_full v)
		{
			float4 sn = mul(_SnowDirection, unity_WorldToObject);
			if (dot(v.normal, sn.xyz) >= _Snow)
			{
				v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;
			}
		}

		void surf (Input IN, inout SurfaceOutput o)
		{
			half4 c = tex2D(_MainTex, IN.uv_MainTex);
			o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump));
			if (dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) >= _Snow)
			{
				o.Albedo = _SnowColor.rgb;
			}
			else
			{
				o.Albedo = c.rgb * _MainColor;
			}
			o.Alpha = 1;
		}
		ENDCG	
	}
	Fallback "Diffuse"

}

LOD (Level of Details) indicates how computationally demanding the subshader is. It’s a convenient way to specify shaders on different platforms. myShader.maximumLOD = 100 makes the only use shader with LOD 100 or lower, so PC and smart phones can both run it.

unity_WorldToObject is a built-in variables. The inverse of current world matrix. tex2D performs a texture lookup in a given 2D sampler. float3 worldNormal; INTERNAL_DATA contains world normal vector if surface shader writes to o.Normal. To get the normal vector based on per-pixel normal map, use WorldNormalVector(IN, o.Normal).