This tutorial teaches you the first steps of creating your own shaders, to help you control the look of your game and optimise the performance of the graphics.
Unity is equipped with a powerful shading and material language called ShaderLabUnity’s declarative language for writing shaders. More info
See in Glossary. In style it is similar to CgFX and Direct3D Effects (.FX) languages - it describes everything needed to display a MaterialAn asset that defines how a surface should be rendered, by including references to the Textures it uses, tiling information, Color tints and more. The available options for a Material depend on which Shader the Material is using. More info
See in Glossary.
Shaders describe properties that are exposed in Unity’s Material Inspector and multiple shader implementations (SubShadersEach shader in Unity consists of a list of subshaders. When Unity has to display a mesh, it will find the shader to use, and pick the first subshader that runs on the user’s graphics card. More info
See in Glossary) targeted at different graphics hardware capabilities, each describing complete graphics hardware renderingThe process of drawing graphics to the screen (or to a render texture). By default, the main camera in Unity renders its view to the screen. More info
See in Glossary state, and vertex/fragment programs to use. Shader programs are written in the high-level Cg/HLSL programming language.
In this tutorial we’ll describe how to write very simple shaders using so-called “fixed function” notation. In the next chapter we’ll introduce vertex and fragment shader programs. We assume that the reader has a basic understanding of OpenGL or Direct3D render states, and has some knowledge of HLSL, Cg, GLSL or Metal shader programming languages.
To create a new shader, either choose Assets > Create > Shader > Unlit Shader from the main menu, or duplicate an existing shader and work from that. The new shader can be edited by double-clicking it in the Project ViewA view that shows the contents of your Assets
folder (Project tab) More info
See in Glossary.
Unity has a way of writing very simple shaders in so-called “fixed-function” notation. We’ll start with this for simplicity. Internally the fixed function shaders are converted to regular vertex and fragment programs at shader import time.
We’ll start with a very basic shader:
Shader "Tutorial/Basic" {
Properties {
_Color ("Main Color", Color) = (1,0.5,0.5,1)
}
SubShader {
Pass {
Material {
Diffuse [_Color]
}
Lighting On
}
}
}
This simple shader demonstrates one of the most basic shaders possible. It defines a color property called Main Color and assigns it a default pink color (red=100% green=50% blue=50% alpha=100%). It then renders the object by invoking a Pass and in that pass setting the diffuse material component to the property _Color and turning on per-vertex lighting.
To test this shader, create a new material, select the shader from the drop-down menu (Tutorial->Basic) and assign the Material to some object. Tweak the color in the Material Inspector and watch the changes. Time to move onto more complex things!
If you open an existing complex shader, it can be a bit hard to get a good overview. To get you started, we will dissect the built-in VertexLit shader that ships with Unity. This shader uses fixed-function pipeline to do standard per-vertex lighting.
Shader "VertexLit" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0.5)
_SpecColor ("Spec Color", Color) = (1,1,1,1)
_Emission ("Emmisive Color", Color) = (0,0,0,0)
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
_MainTex ("Base (RGB)", 2D) = "white" { }
}
SubShader {
Pass {
Material {
Diffuse [_Color]
Ambient [_Color]
Shininess [_Shininess]
Specular [_SpecColor]
Emission [_Emission]
}
Lighting On
SeparateSpecular On
SetTexture [_MainTex] {
constantColor [_Color]
Combine texture * primary DOUBLE, texture * constant
}
}
}
}
All shaders start with the keyword ShaderA small script that contains the mathematical calculations and algorithms for calculating the Color of each pixel rendered, based on the lighting input and the Material configuration. More info
See in Glossary followed by a string that represents the name of the shader. This is the name that is shown in the InspectorA Unity window that displays information about the currently selected GameObject, Asset or Project Settings, alowing you to inspect and edit the values. More info
See in Glossary. All code for this shader must be put within the curly braces after it: { } (called a block).
The shader is composed of a Properties block followed by SubShader blocks. Each of these is described in sections below.
At the beginning of the shader block you can define any properties that artists can edit in the Material Inspector. In the VertexLit example the properties look like this:
The properties are listed on separate lines within the Properties block. Each property starts with the internal name (Color, MainTex). After this in parentheses comes the name shown in the inspector and the type of the property. After that, the default value for this property is listed:
The list of possible types are in the Properties Reference. The default value depends on the property type. In the example of a color, the default value should be a four component vector.
We now have our properties defined, and are ready to start writing the actual shader.
Before we move on, let’s define the basic structure of a shader file.
Different graphic hardware has different capabilities. For example, some graphics cards support fragment programs and others don’t; some can lay down four textures per pass while the others can do only two or one; etc. To allow you to make full use of whatever hardware your user has, a shader can contain multiple SubShaders. When Unity renders a shader, it will go over all subshaders and use the first one that the hardware supports.
Shader "Structure Example" {
Properties { /* ...shader properties... }
SubShader {
// ...subshader that requires DX11 / GLES3.1 hardware...
}
SubShader {
// ...subshader that might look worse but runs on anything :)
}
}
This system allows Unity to support all existing hardware and maximize the quality on each one. It does, however, result in some long shaders.
Inside each SubShader block you set the rendering state shared by all passes; and define rendering passes themselves. A complete list of available commands can be found in the SubShader Reference.
Each subshader is a collection of passes. For each pass, the object geometry is rendered, so there must be at least one pass. Our VertexLit shader has just one pass:
// ...snip...
Pass {
Material {
Diffuse [_Color]
Ambient [_Color]
Shininess [_Shininess]
Specular [_SpecColor]
Emission [_Emission]
}
Lighting On
SeparateSpecular On
SetTexture [_MainTex] {
constantColor [_Color]
Combine texture * primary DOUBLE, texture * constant
}
}
// ...snip...
Any commands defined in a pass configures the graphics hardware to render the geometry in a specific way.
In the example above we have a Material block that binds our property values to the fixed function lighting material settings. The command Lighting On turns on the standard vertex lighting, and SeparateSpecular On enables the use of a separate color for the specular highlight.
All of these command so far map very directly to the fixed function OpenGL/Direct3D hardware model. Consult OpenGL red book for more information on this.
The next command, SetTexture, is very important. These commands define the textures we want to use and how to mix, combine and apply them in our rendering. SetTexture command is followed by the property name of the texture we would like to use (_MainTex here) This is followed by a combiner block that defines how the texture is applied. The commands in the combiner block are executed for each pixelThe smallest unit in a computer image. Pixel size depends on your screen resolution. Pixel lighting is calculated at every screen pixel. More info
See in Glossary that is rendered on screen.
Within this block we set a constant color value, namely the Color of the Material, _Color. We’ll use this constant color below.
In the next command we specify how to mix the texture with the color values. We do this with the Combine command that specifies how to blend the texture with another one or with a color. Generally it looks like this: Combine ColorPart, AlphaPart
Here ColorPart and AlphaPart define blending of color (RGB) and alpha (A) components respectively. If AlphaPart is omitted, then it uses the same blending as ColorPart.
In our VertexLit example: Combine texture * primary DOUBLE, texture * constant
Here textureAn image used when rendering a GameObject, Sprite, or UI element. Textures are often applied to the surface of a mesh to give it visual detail. More info
See in Glossary is the color coming from the current texture (here _MainTex). It is multiplied (*) with the primary vertex color. Primary color is the vertex lighting color, calculated from the Material values above. Finally, the result is multiplied by two to increase lighting intensity (DOUBLE). The alpha value (after the comma) is texture multiplied by constant value (set with constantColor above). Another often used combiner mode is called previous (not used in this shader). This is the result of any previous SetTexture step, and can be used to combine several textures and/or colors with each other.
Our VertexLit shader configures standard vertex lighting and sets up the texture combiners so that the rendered lighting intensity is doubled.
We could put more passes into the shader, they would get rendered one after the other. For now, though, that is not nessesary as we have the desired effect. We only need one SubShader as we make no use of any advanced features - this particular shader will work on any graphics card that Unity supports.
The VertexLit shader is one of the most basic shaders that we can think of. We did not use any hardware specific operations, nor did we utilize any of the more special and cool commands that ShaderLab and Cg/HLSL has to offer.
In the next chapter we’ll proceed by explaining how to write custom vertex & fragment programs using Cg/HLSL language.