Cg Tutorial to Unity

From Unify Community Wiki
Jump to: navigation, search

Contents

Description

Ippokratis 11:07, 30 September 2011 (PDT)
This is a port of the source code from Cg tutorial ( http://developer.nvidia.com/node/76 ) to Shaderlab.
Users are expected to read the original material and find here some help and explanations on how this code could be ported to Unity.
For each pair of vertex and fragment shaders, a unity shaderlab is provided.
There are some caveats in porting, hopefully here we will overcome some.

Chapter 2

In chapter 2 the code is in the examples 2-1 and 2-3.

Example 2-1

struct C2E1v_Output 
{
  float4 position : POSITION;
  float4 color    : COLOR;
};
 
C2E1v_Output C2E1v_green(float2 position : POSITION)
{
  C2E1v_Output OUT;
  OUT.position = float4(position, 0, 1);
  OUT.color    = float4(0, 1, 0, 1);  // RGBA green
  return OUT;
}

Example 2-3

struct C2E2f_Output 
{
  float4 color : COLOR;
};
 
C2E2f_Output C2E2f_passthrough(float4 color : COLOR)
{
  C2E2f_Output OUT;
  OUT.color = color;
  return OUT;
}

The above two pieces combined to obtain a unity shader C2E1:

//Shaders start with the Shader keyword followed by shader's name in ""
//Custom/C2E1 means that the shader name is C2E1 and is placed in a shadr group called Custom
//Refer here : http://unity3d.com/support/documentation/Components/SL-Shader.html
Shader "Custom/C2E1" 
{
	//Refer here : http://unity3d.com/support/documentation/Components/SL-SubShader.html
	SubShader 
	{
		//Refer here : http://unity3d.com/support/documentation/Components/SL-Pass.html
		Pass 
		{
		//Refer here : http://unity3d.com/support/documentation/Components/SL-ShaderPrograms.html
		CGPROGRAM
		// The vertex shader name should match the vertex shader function name
		#pragma vertex C2E1v_green
		// The fragment shader name should match the fragment shader function name
		#pragma fragment C2E2f_passthrough
 
 
		struct C2E1v_Output 
		{
			float4 position : POSITION;
			float4 color : COLOR;
		};
 
		C2E1v_Output C2E1v_green(float2 position : POSITION)
		{
		  C2E1v_Output OUT;
		  OUT.position = float4(position,0,1);
		  OUT.color    = float4(0, 1, 0, 1);  // RGBA green
		  return OUT;
		}		
 
		struct C2E2f_Output
		{
			float4 color : COLOR;
		};
		C2E2f_Output C2E2f_passthrough(float4 color : COLOR)
		{
			C2E2f_Output OUT;
			OUT.color = color;
			return OUT;
		}
 
		ENDCG
		}
	}
}

To see the above shader in action do the following :

  • In the Project View
    • Create a new shader, name it C2E1 and copy / paste the above code
    • Create a new material, name it C2E1 and assign to it the above shader
  • In the Hierarchy View
    • Create a cube and drag the C2E1 material on it.

The expected result is to see a green rectangle.

caption

Code analysis

The code provided in chapter two comprises two shaders:

  • a vertex shader called C2E1v_green
  • a fragment shader called C2E2f_passthrough

To construct from them a shader in unity, we will first analyze them.
Vertex shader takes data from Unity, processes them and passes them down to the graphics pipeline. ( Simplified view )
What data get in ? Vertex data. Each vertex in the mesh has attributes. Those attributes are :

//From UnityCG.cginc
struct appdata_full {
    float4 vertex : POSITION;// The position of the vertex in object space
    float4 tangent : TANGENT;// The tangent of the vertex
    float3 normal : NORMAL;// The normal of the vertex
    float4 texcoord : TEXCOORD0;// base texture uv coordinates of the vertex
    float4 texcoord1 : TEXCOORD1;// second texture uv2 coordinates of the vertex
    fixed4 color : COLOR; // vertex color
    //As long as you call one of the semantics that unity supports, POSITION, TANGENT, NORMAL, TEXCOORD0, TEXCOORD1, COLOR it's ok.
};

We can feed with some of this data the vertex shader.
The vertex shader has a "main function" that processes this data. It runs once for each vertex and it fills a struct with data to be passed down to the graphics pipeline. In this example, the "main function" is :

// function name : C2E1v_green, 
//   takes a float2 named position with a POSITION semantic
//   and outputs a C2E1v_Output ( see below ).
C2E1v_Output C2E1v_green(float2 position : POSITION)
{
  // Create a new struct of type C2E1v_Output named OUT
  C2E1v_Output OUT;
  // Fill the position member of the OUT struct 
  //   with a float4, first two floats are from Unity app ( the "float2 position : POSITION" )
  //   other two are 0, 1  
  OUT.position = float4(position,0,1);
  // Fill the color member of the struct 
  //   with a float4, whose value corresponds to green
  OUT.color    = float4(0, 1, 0, 1);  // RGBA green
  // // return the OUT with new data.
  return OUT;
        }

To send this data down the graphics pipeline you create a struct that describes what gets out.

//Declare a struct called C2E1v_Output
struct C2E1v_Output 
{
  //This struct has two members.
  //First member is a float4 ( type ), called position, that has the POSITION semantic
  //Refer to http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter02.html for an explanation on 
  //  what is the meaning of identifier, semantic, type is.
  float4 position : POSITION;
  float4 color    : COLOR;
  //As long as you call one of the semantics that unity supports, POSITION, TANGENT, NORMAL, TEXCOORD0, TEXCOORD1, COLOR it's ok.
  //Remember to place the colon ";"
};

So, for each vertex, we retrieve its position and assign a color.
The fragment shader gets data from the vertex shader, processes them and sends them to the frame buffer.
This fragment shader " main function " is described below :

// function name : C2E2f_passthrough, 
//   takes a float4 named color with a COLOR semantic
//   and outputs a C2E2f_Output ( see below ).
C2E2f_Output C2E2f_passthrough(float4 color : COLOR)
{
  // Create a new struct of type C2E2f_Output named OUT
  C2E2f_Output OUT;
  // Fill the color member of the struct 
  //   with the float4 color from function input
  OUT.color = color;
  // return the OUT with new data.
  return OUT;
}

The data is packed in a struct

//Declare a struct called C2E2f_Output
struct C2E2f_Output
{
  //First member is a float4 ( type ), called color, that has the COLOR semantic
  float4 color : COLOR;
};

And it is send down to the frame buffer.

Code porting and tweaking

We have already integrated the above code in a Unity shader. Now we will see another example that does the same thing but uses a different naming approach,
along with some minor tweaks. This approach is nearer to the style that many people in the forums
choose to code.

//Shaders start with the Shader keyword followed by shader's name in ""
//Custom/C2E1tweaked means that the shader name is C2E1tweaked and is placed in a shadr group called Custom
//Refer here : http://unity3d.com/support/documentation/Components/SL-Shader.html
Shader "Custom/C2E1tweaked" 
{
	//Refer here : http://unity3d.com/support/documentation/Components/SL-SubShader.html
	SubShader 
	{
		//Refer here : http://unity3d.com/support/documentation/Components/SL-Pass.html
		Pass 
		{
		//Refer here : http://unity3d.com/support/documentation/Components/SL-ShaderPrograms.html
		CGPROGRAM
		// The vertex shader name should match the vertex shader function name
		//    Makes sense to name it vert
		#pragma vertex vert
		// The fragment shader name should match the fragment shader function name
		//    Makes sense to name it frag
		#pragma fragment frag
 
		// Declare a struct that gets data from application (Unity) to the vertex shader
		//    called a2v
		//    Refer : http://unity3d.com/support/documentation/Components/SL-VertexProgramInputs.html 
		struct a2v
		{
			//a little catch : the identifier for the POSITION semantic must be vertex only
			//  for NORMAL normal only etc. 
			//  Refer : http://unity3d.com/support/documentation/Components/SL-VertexProgramInputs.html 
			//  ( second paragraph, after the first example )   
			float4 vertex:POSITION;
		};
		// We call the struct that takes data from vertex to fragment v2f		
		struct v2f 
		{
			float4 position : POSITION;
			float4 color : COLOR;
		};
		// The vert function takes a a2v struct called In and returns a v2f struct called Out		 
		v2f  vert(a2v In)
		{
		  v2f Out;
                  // for some reason we have to crop In.vertex off 2 dimensions
		  Out.position = float4(float2(In.vertex), 0,1);
		  Out.color    = float4(0, 1, 0, 1);  // RGBA green
		  return Out;
		}		
		// It is not necessary to declare a struct for the fragment output
		// We specify instead that the frag function takes a v2f struct that we name In
		//   ( contains the data from the vertex shader v2f Out )
		//   and returns a float4 with a COLOR semantic 
		float4 frag( v2f In ): COLOR
		{
			return In.color;
		}
 
		ENDCG
		}
	}
}

Chapter 3

In chapter 3, there are 7 new fragment and vertex shaders.
Remember, the f ( e.g. C2E2f_passthru ) means they are fragment shaders,
while the v ( e.g. C3E1v_anycolor ) means they are vertex shaders. After having a look at the examples in the Cg toolkit ( not the manual, the file ), they are combined in 6 unity shaders in the following way :

  • C3E1 : uniform parameter
    • C3E1v_anycolor
    • C2E2f_passthru
  • C3E2 : varying parameter
    • C3E2v_varying
    • C2E2f_passthru
  • C3E3 : texture sampling
    • C3E2v_varying
    • C3E3f_texture
  • C3E4 : vertex twisting
    • C3E4v_twist
    • C2E2f_passthru
  • C3E5 : two texture accesses
    • C3E5v_twoTextures
    • C3E6f_twoTextures
  • C3E6 : two texture double vision
    • C3E5v_twoTextures
    • C3E7f_twoTextures


Shader C3E1 uniform parameter

//Shaders start with the Shader keyword followed by shader's name in ""
//Custom/C2E1 means that the shader name is C2E1 and is placed in a shadr group called Custom
//Reference : http://unity3d.com/support/documentation/Components/SL-Shader.html
Shader "Custom/C3E1" 
{
	Properties //Reference : http://unity3d.com/support/documentation/Components/SL-Properties.html
	{
		//There is some conflict when naming this to constantColor
		// so we choose the _constantColor name instead
		_constantColor ("Refraction color", Color)  = (.34, .85, .92, 1) // color
    }
	//Reference  : http://unity3d.com/support/documentation/Components/SL-SubShader.html
	SubShader 
	{
		//Reference  : http://unity3d.com/support/documentation/Components/SL-Pass.html
		Pass 
		{
		//Reference  : http://unity3d.com/support/documentation/Components/SL-ShaderPrograms.html
		CGPROGRAM
		// The vertex shader name should match the vertex shader function name
		#pragma vertex C3E1v_anyColor
		// The fragment shader name should match the fragment shader function name
		#pragma fragment C2E2f_passthrough
 
 
		struct C3E1v_Output 
		{
			float4 position : POSITION;
			float4 color    : COLOR;
		};
 
		C3E1v_Output C3E1v_anyColor(float2 position : POSITION,		
									uniform float4 _constantColor)
		{
			C3E1v_Output OUT;
			OUT.position = float4(position, 0, 1);
			OUT.color = _constantColor;  // Some RGBA color
			return OUT;
		}		
 
		struct C2E2f_Output
		{
			float4 color : COLOR;
		};
 
		C2E2f_Output C2E2f_passthrough(float4 color : COLOR)
		{
			C2E2f_Output OUT;
			OUT.color = color;
			return OUT;
		}
 
		ENDCG
		}
	}
}


Shader C3E2 varying parameter


Shader C3E3 texture sampling


//Shaders start with the Shader keyword followed by shader's name in ""
//Custom/C2E1 means that the shader name is C2E1 and is placed in a shadr group called Custom
//Reference : http://unity3d.com/support/documentation/Components/SL-Shader.html
Shader "Custom/C3E3" 
{
	Properties //
	{
		decal ("Base (RGB)", 2D) = "white" {}
    }
	//Reference  : http://unity3d.com/support/documentation/Components/SL-SubShader.html
	SubShader 
	{
		//Reference  : http://unity3d.com/support/documentation/Components/SL-Pass.html
		Pass 
		{
		//Reference  : http://unity3d.com/support/documentation/Components/SL-ShaderPrograms.html
		CGPROGRAM
		// The vertex shader name should match the vertex shader function name
		#pragma vertex C3E2v_varying
		// The fragment shader name should match the fragment shader function name
		#pragma fragment C3E3f_texture
 
 
 
		struct v2f 
		{
			float4 position : SV_POSITION;
			float4 color    : COLOR;
			float4 texCoord  : TEXCOORD0;
		};
 
		struct a2v
		{
			float4 vertex   : POSITION;
			float4 color    : COLOR;
			float4 texcoord : TEXCOORD0;
		};		
 
		v2f  C3E2v_varying(a2v In)
		{
			v2f  OUT;
			OUT.position = float4(In.vertex.xy, 0, 1);
			OUT.color    = In.color;
			//o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
			OUT.texCoord  = In.texcoord ;
			return OUT;
		}
 
		struct C3E3f_Output 
		{
			float4 color : COLOR;
		};
 
		C3E3f_Output C3E3f_texture(float4 texcoord : TEXCOORD0,
									sampler2D decal)
		{
			C3E3f_Output OUT;
			OUT.color = tex2D(decal, texcoord.xy);
			return OUT;
		}
 
		ENDCG
		}
	}
}


//Shaders start with the Shader keyword followed by shader's name in ""
//Custom/C2E1 means that the shader name is C2E1 and is placed in a shadr group called Custom
//Reference : http://unity3d.com/support/documentation/Components/SL-Shader.html
Shader "Custom/C3E3tweaked" 
{
	Properties //
	{
		decal ("Base (RGB)", 2D) = "white" {}
    }
	//Reference  : http://unity3d.com/support/documentation/Components/SL-SubShader.html
	SubShader 
	{
		//Reference  : http://unity3d.com/support/documentation/Components/SL-Pass.html
		Pass 
		{
		//Reference  : http://unity3d.com/support/documentation/Components/SL-ShaderPrograms.html
		CGPROGRAM
		// The vertex shader name should match the vertex shader function name
		#pragma vertex vert
		// The fragment shader name should match the fragment shader function name
		#pragma fragment frag
 
 
 
		struct v2f 
		{
			float4 position : SV_POSITION;
			float4 texcoord  : TEXCOORD0;
		};
 
		struct a2v
		{
			float4 vertex   : POSITION;
			float4 texcoord : TEXCOORD0;
		};		
 
		v2f  vert(a2v In)
		{
			v2f  OUT;
			OUT.position = float4(In.vertex.xy, 0, 1);
			OUT.texcoord  = In.texcoord ;
			return OUT;
		}
 
		struct C3E3f_Output 
		{
			float4 color : COLOR;
		};
 
 
		sampler2D decal;
		float4 frag( v2f In ): COLOR
		{
			float4 color = tex2D(decal, In.texcoord.xy);
			return color;
		}
 
		ENDCG
		}
	}
}


//Shaders start with the Shader keyword followed by shader's name in ""
//Custom/C2E1 means that the shader name is C2E1 and is placed in a shadr group called Custom
//Reference : http://unity3d.com/support/documentation/Components/SL-Shader.html
Shader "Custom/C3E3tweaked2" 
{
	Properties //
	{
		_MainTex ("Base (RGB)", 2D) = "white" {}
    }
	//Reference  : http://unity3d.com/support/documentation/Components/SL-SubShader.html
	SubShader 
	{
		//Reference  : http://unity3d.com/support/documentation/Components/SL-Pass.html
		Pass 
		{
		//Reference  : http://unity3d.com/support/documentation/Components/SL-ShaderPrograms.html
		CGPROGRAM
		// The vertex shader name should match the vertex shader function name
		#pragma vertex vert
		// The fragment shader name should match the fragment shader function name
		#pragma fragment frag
		#include "UnityCG.cginc"
 
 
		struct v2f 
		{
			float4 position : SV_POSITION;
			float2 texcoord  : TEXCOORD0;
		};
 
		struct a2v
		{
			float4 vertex   : POSITION;
			float4 texcoord : TEXCOORD0;
		};		
		//Necessary for line 48 to work
		float4 _MainTex_ST;
		v2f  vert(a2v In)
		{
			v2f  OUT;
			OUT.position = float4(In.vertex.xy, 0, 1);
 
			//From unityCG.cginc : Transforms 2D UV by scale/bias property
			//  #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
			//  when user enters TRANSFORM_TEX(texUvCoords, texName) process it as (texUvCoords.xy * texName_ST.xy + texName_ST.zw)
			//  This way, we can use the offset - tiling properties of a material
			//  Reference : http://unity3d.com/support/documentation/Components/class-Material.html
			OUT.texcoord = TRANSFORM_TEX(In.texcoord, _MainTex);
			return OUT;
		}
 
		struct C3E3f_Output 
		{
			float4 color : COLOR;
		};
 
 
		sampler2D _MainTex;
		float4 frag( v2f In ): COLOR
		{
			float4 color = tex2D(_MainTex, In.texcoord.xy);
			return color;
		}
 
		ENDCG
		}
	}
}


Shader C3E4 : vertex twisting


For this example to work properly, we'll either need a cube or a plane with a higher tessellation. To achieve this we'll need the CreatePlane script from Michael Garforth.
Create your plane with 64x64 subdivisions and add a material to it. Link the material to the C3E4_Twisting shader:

Shader "Custom/C3E4_twisting" {
	Properties
	{
		twisting ("twisting", Float) = 0.5
		_color ("color", Color) = (1,1,1,1)
    }
	SubShader 
	{
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
 
			struct v2f
			{
				float4 pos : POSITION;
				float4 color : COLOR;
			};
			// This differs from the original C3E4 example as I exposed the _color param instead of letting Unity provide it itself
			v2f vert(	float2 pos : POSITION,
						uniform float4 _color,
						uniform float twisting)
			{
				v2f o;
				float angle = twisting * length(pos);
				float cosLength, sinLength;
				sincos(angle, sinLength, cosLength);
				o.pos[0] = cosLength * pos[0] +
						-sinLength * pos[1];
				o.pos[1] = sinLength * pos[0] +
						cosLength * pos[1];
				o.pos[2] = 0;
				o.pos[3] = 1;
				o.color = _color;
				return o;
 
			}
			// regular passthrough fragment shader
			float4 frag(v2f i) : COLOR
			{
				return i.color;
			}
		ENDCG
		}
	}	
}

Shader C3E5 : two textures accesses


Shader C3E6 : two textures double vision


Shader "Custom/C3E5_DoubleVision"
{
	Properties
	{
		_MainTex ("Diffuse", 2D) = "white" {}
		//Unity does not support 2-elements vectors. But that's OK since the implicit casts work just fine.
		_leftSeparation ("Left Separation", Vector) = (-0.5,0,0,0)
		_rightSeparation ("Right Separation", Vector) = (0.5,0,0,0)
		//Let the user control the lerp value
		_lerpValue ("Linear Interpolation Value", Float) = 0.5
    }
	SubShader 
	{
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
 
			void vert(	float2 pos : POSITION,
						float2 texCoord : TEXCOORD0,
 
						out float4 oPos : POSITION,
						out float2 leftTexCoord : TEXCOORD0,
						out float2 rightTexCoord : TEXCOORD1,
 
						uniform float2 _leftSeparation,
						uniform float2 _rightSeparation)
			{
				oPos = float4(pos, 0, 1);
				leftTexCoord = texCoord + _leftSeparation;
				rightTexCoord = texCoord + _rightSeparation;
 
			}
			void frag(  float2 leftTexCoord : TEXCOORD0,
						float2 rightTexCoord : TEXCOORD1,
 
						out float4 color : COLOR,
 
						uniform sampler2D _MainTex,
						//Get the lerp value we exposed earlier
						uniform float _lerpValue)
			{
				float4 leftColor = tex2D(_MainTex, leftTexCoord);
				float4 rightColor = tex2D(_MainTex, rightTexCoord);
				color = lerp(leftColor, rightColor, _lerpValue);
			}
 
		ENDCG
		}
	}	
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Tools