Introduction to Shaders in Unity

Ever wondered about shaders in Unity? In this tutorial, you’ll learn what shaders are, how to display vertex colors and how to animate within shaders. By Joseph Hocking.

4.7 (25) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

The SubShader block

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

The Subshader code block is where the majority of the Shader code goes. The first two lines in this block declare identifying tags recognized by Unity and set a value used by Unity’s Level-of-Detail (LOD) system.

In this case, the identifying tags declare that the Shader is not see-through. Technically, there can be multiple Subshader blocks, but you won’t get into that level of complexity in this tutorial.

The CGPROGRAM block

		CGPROGRAM
		// Physically-based standard lighting model,
                // and enable shadows on all light types
		#pragma surface surf Standard fullforwardshadows

		// Use Shader model 3.0 target to get nicer looking lighting
		#pragma target 3.0

These lines declare several important aspects of the code to follow. First, they indicate the following code uses the Cg language. There are several different programming languages for writing Shaders. Everything up to this point was using Unity’s language, ShaderLab.

Meanwhile, #pragma directives set up configuration values. Going over the first one in detail: surface tells the Shader compiler that this is a Surface Shader. Again, remember that other types include Fragment and Vertex Shaders.

surf is the name of the main shading function below. Standard declares the lighting model you want – other lighting models include Lambert and Blinn-Phong, but the Standard physically-based lighting is the best-looking. fullforwardshadows activates dynamic shadows for this Shader.

MainTex property variable

		sampler2D _MainTex;

This declares a variable corresponding to one of the properties. While it seems a tad redundant, you must declare the variable here for the Cg code to use that property.

These variables can be one of several types including sampler2D for a texture image, and fixed/half/float for numbers. Those three are numbers of increasing precision, and you should use the least precision that works.

Number values can have a suffix number to make a vector. For example, fixed4 indicates four numbers. You access values in the vector with either .xyzw or .rgba properties.

For example, c.rgb in the Shader code extracts the first three numbers from the fixed4 called c.

You must declare all the properties as variables in the Cg code. You can see the other property names a bit later in the tutorial. Why Unity’s template code has them written apart from each other, as opposed to one unified list, is a mystery, but it also doesn’t really matter.

Inputs and property variables

		struct Input {
			float2 uv_MainTex;
		};

		half _Glossiness;
		half _Metallic;
		fixed4 _Color;

		// Add instancing support for this Shader. You need to check
                // 'Enable Instancing' on materials that use the Shader.
		// See https://docs.unity3d.com/Manual/GPUInstancing.html for
                // more information about instancing.
		// #pragma instancing_options assumeuniformscaling
		UNITY_INSTANCING_BUFFER_START(Props)
			// put more per-instance properties here
		UNITY_INSTANCING_BUFFER_END(Props)

This code block declares a data struct called Input and lists the values in it. The only input values in the template code are the UV coordinates of the main texture, but there are several input values that Shaders can access. The graphics data will pass the input values you declare here to the Shader.

The main shader function

		void surf (Input IN, inout SurfaceOutputStandard o) {
			// Albedo comes from a texture tinted by color
			fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			// Metallic and smoothness come from slider variables
			o.Metallic = _Metallic;
			o.Smoothness = _Glossiness;
			o.Alpha = c.a;
		}
		ENDCG
	}

surf is the main shading function, which you declare in the #pragma line above. The first parameter is the Input struct, while the other parameter is the output that the function writes to.

You’ll notice that the output structure has parameters like .Metallic and .Smoothness that you set using the Shader’s properties.

The one line in surf that isn’t a straight assignment of an input number to an output value is fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;.

Unity provides tex2D to look up the color in a texture at a given coordinate. When writing a Fragment Shader, you need to explicitly include Unity’s Shader library, but Surface Shaders include the library automatically.

FallBack shader

	FallBack "Diffuse"
}

After the Cg code, you declare a fallback Shader, although it isn’t required. This directs Unity to use the fallback Shader if the graphics hardware can’t run any custom Subshaders, typically because it’s an older graphics card that doesn’t support the Shader’s features.

All right, now that you understand the structure of Shader code, it’s time to address the visual issues identified at the beginning of this section.

Adding Vertex Color to a Surface Shader

The first issue you wanted to fix in the scene was that the island has hard, square edges.

You can see the edges of the mesh quite clearly right now, but real beaches appear to fade out to the color of the water.

There are many ways to achieve this look in a game, but one simple approach uses vertex color.

To understand what vertex colors are, realize that a “vertex” is simply a bundle of data. This data always includes the position of the vertex, but you can also include additional options.

Texture coordinates are a common addition, with the coordinates (identified with the letters UV, instead of XY) providing numbers that you use when working with textures. Well, color is another option, providing numbers that you use when calculating the Shader’s color output.

For example, if you assign a vertex to red, then the polygon using that vertex will have a red tint. Furthermore, if the vertices of a polygon have different colors, then those colors get interpolated across the polygon’s face.

Colors interpolated across the face of a triangle

This island mesh already has vertex colors assigned, but you can’t see them because Unity’s standard Shader does not handle vertex colors.

Specifically, vertices under the water have a darker tint, while the rest of the island’s vertices are white. These colors would create a pleasing gradient along the edge of the beach if they were visible, so your next task is to write a custom Shader that handles vertex color.