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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Introduction to Shaders in Unity
25 mins
- What Are Shaders?
- Understanding Types of Shaders
- Writing a Custom Shader
- Looking at the Default Template for a Custom Shader
- Properties
- The SubShader block
- The CGPROGRAM block
- MainTex property variable
- Inputs and property variables
- The main shader function
- FallBack shader
- Adding Vertex Color to a Surface Shader
- Creating a Custom Shader for Vertex Color
- Animating the Water Texture
- Creating an Unlit Shader
- Where to Go From Here?
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.
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.