Inside out : A Shader’s Anatomy

Let us examine the varieties of shaders out there and see what are they made of. We will also be writing our very first shader !

Hello and Welcome!

So, in the previous part, you learned all about meshes, polygons, pipelines and the lot. Understanding about those will really help you, as you write your own shaders and you will be crystal clear about what they are doing.

NOTICE : Before continuing on with this lesson, though, please head over to the short Chapter 01 quiz. Attempt all the questions and do not cheat! If you pass it, come back here and continue. If you don’t, well, go back to the previous chapter and also comment what you are not clear about.

So, first and foremost, let’s see the types of shaders there are and which ones will we be learning about.

The types of Shaders

Till now you have seen a few shaders being mentioned in this series. Vertex shaders, pixel shaders, and geometry shaders. Let’s see what each shader is, and what it’s capable of:

  1. Fixed Function Shader:

Remember when we talked about the fixed-function pipeline in the previous chapter? Yes, these were the shaders that were hard-coded(written in such a way that cannot be modified later on) into that pipeline. These are now out of date and not really important for us, so we’ll not be seeing them further.

2. Vertex Shader:

Vertex shaders run once for each vertex of the 3D model. Their main job is to transform the 3D coordinates of the 3D model in such a way that they can be displayed on our flat 2D computer screens. As you might have guessed from the name itself, it manipulates and modifies the properties of the vertices, like their positions, color, textures applied, etc.

3. Geometry Shader:

The Geometry shader is a comparatively new shader, that can be used on the run time (while the game is actually running) to generate geometrical shapes, like lines, edges and polygons.

Check out this helpful slide. (Source)

4. Pixel / Fragment Shader:

The Pixel (or the Fragment) shader will calculate the color of each single pixel, its depth value, and so on. It calculates the colors through various techniques, which we will be covering later on.

Pixel shaders can range from simple to complicated. They can return a single color value for a pixel, or multiple values (for special effects), depending on the shader code. They are used for advanced effects like Shadows, Translucency, and also post-processing (adding special effects in video games and movies).

These are the data that’s manipulated by Vertex and Fragment shaders. (Source)

5. Surface Shader:

The Surface shader is a special type of shader, which is present in the Unity Game Engine.

They are the highly abstracted type of shaders, which means that Unity has pre-written most of the complex code for you. You will just have to write a little code to get these working and achieve the effects you want.

We generally use Surface shaders, when we need realistic light simulations on the surface of our 3D object. And as Unity has written most of the code for you, you don’t need to write complex stuff like the lighting models (how the light will behave) or lighting calculations. It’s already taken care of. This makes this shader a lot easier to use, but a lot less flexible too.

Andy Touch, of Unity Technologies in this wonderful Unite 2016 talk has rightly referred a Surface shader as a pre-made car.

Easy Peasy. That’s what a surface shader is. (Source)

Ladies and Gentlemen, this is exactly what a surface shader is. It’s like a pre-made car that you can just hop on to and drive around. Sure, you will have to learn how to drive a car, but you won’t need to build the car’s engine by yourself. It’s instant and easy to use. And in most cases, that’s sufficient.

Lastly, please remember this point: A surface shader will not modify the mesh of your 3D model, it will just work on the faces of the polygon, i.e the surface. So, if you want advanced effects to manipulate the mesh of your model, you will have to look elsewhere.

6. Vertex and Fragment Shader

We’ve discussed what vertex and fragment shaders are, right? Now combine them into one single shader. This is actually what unity does with the Vertex and the Fragment shaders. Unity handles both of them at once, and all of the shader code regarding these both shaders is in a single file.

The vertex shader calculates the 2D positions of the vertices and the Fragment shader will color them. (Source)

In these types of shaders, you will have to code your own lighting model. There is much less abstraction here. Again, referring to the Unite 2016 talk, Vertex and Fragment shaders are like the car engine that you will have to build all by yourself. There’s no hand-holding or spoon-feeding in this one.

It’s hard work, but it pays well.

These types of shaders are used for more complex operations and effects that cannot be done and achieved by Surface shaders. We will have to do our own lighting calculations, make our own lighting models and write a lot more code. But, the end result is that you will end up having more control over your shader and be able to achieve much more complex graphical effects.

How we will be writing our Shaders

There are some good options when it comes to the choice of writing your shaders. You can use OpenGL, HLSL, ShaderLab ,CG, and other various tools and APIs to write them.

We will be using the following:

Unity Engine : The game engine in which we will try out our shaders.

ShaderLab: A language unique to Unity, used for structuring of the Shader code.

CG: Short for Computer Graphics, this is the actual code that will make the shader do its work.

Now, the reasons that I use this and not the OpenGL framework is because:

  1. Unity is the most popular way of making games right now. Knowledge of writing shaders in it will benefit a lot of people.
  2. ShaderLab and CG are really easy to learn and have a familiar C language like syntax. So you can pick it up really, really quickly.
  3. I don’t know OpenGL. (yet!)

So, now that’s clear and done, fire up your Unity Editor and let’s get Shading!

Note : Any versions of Unity, later than Unity 5.5 would be eligible to follow this tutorial.

Getting Started : Writing your first Shader!

Structure of Shaders in Unity

Before writing our very 1st Shader, it will be certainly helpful to see what the structure of our shader’s code will look like.

And before that, we need to be clear on how exactly a Shader in Unity is written.

A shader is written using two languages in Unity:

  1. Shader Lab : It is a language that is used to structure the shader code. All Shaders files in Unity are written in a declarative language called “ShaderLab”.
  2. CG: The actual “shader code” is written inside the CGPROGRAM blocks, present inside the same shader file as the ShaderLab code. CG is actually a programming language, developed by NVIDIA for writing shaders.

So, it can be visualized as:

Shader Lab essentially “wraps” the CG code inside of it. (Source)

A basic skeleton of any Shader code written in Unity is:

Now, there are some new things going on here. Let’s take a look at them:

Shader “Category/Name” //Name the shader and give it a category

This line, lets you name the Shader and its category, how will it be displayed when you are choosing the shader. For example, if I wanted to create an Explosions shader for Mobile on the Android Platform, I would write the line like this, which would make things categorized and easy:

Shader “Mobile Shaders/Android/Explosions”

Moving on, the Properties block simply let you write the properties of the 3D object you would be modifying through the material. They are similar to the public field in Unity C#, and anything inside it will be visible in the inspector.

For example, writing this :

Properties
 {
 _myColor(“Example Color”, Color) = (1,1,1,1)
 _myRange(“Multiplier Range”, Range(0,5)) = 1
 _myTexture(“Example Texture ”, 2D) = “white” {}
 _myFloat(“Example Float”, Float) = 0.5
 
 }

Would result into this, in the inspector:

You can now easily set these values via the Inspector only.

(All the code contained inside the Properties block above will be explained shortly!)

And, we then see:

Subshader //Start writing the actual Shader code, which does stuff
{
CGPROGRAM //signal the starting of the CG program
//Surface Shader function
//OR
//Vertex and Fragment Shader function
ENDCG //signal the end of the CG program code
}

This is the Subshader block ,that merely declares that the CG programs will be written here. The CG program will contain variable references to the properties we’ve described before, in the Properties block. And it will contain the shader functions to modify those properties.

The starting is signalled by CGPROGRAM and the end by the ENDCG line.

Finally, we have:

Fallback “NameOfAlternative”
//Use the alternative shader for older graphics card, incase our new custom shader is not supported.

This line is the “Fallback” line, which is used for activating other shaders, incase the one we have written is too advanced for the GPU to handle. Here, the alternative shader to be used is written inside the braces “” .

This is generally helpful for older GPUs, which cannot handle the effects of the modern shaders.

Finally : Writing your First Shader !

Now that we are aware of the various types of Shaders and their code structure, let’s get coding!

Open up your Unity Editor, and follow these following steps to open up your own Shader file:

  1. Create and name a new 3D project in the Unity welcome screen.
  2. Go to the Project Pane, and Create a new folder. Name it Shaders. Your Project pane should look like this:

3. Inside of your Shader folder,

Right Click>Create>Shader>Standard Surface Shader

4. You can name the shader anything, it will not matter. The actual shader name would be defined on the shader code itself.

5. Open up the newly created shader file, and you will see a whole bunch of code, like this:

Aw, unity has already done a lot of work for us. Now, delete it all!

You won’t be needing any of that. Delete them all, and we will write our own shader, from the scratch.

6. Now, in the empty file, write the basic skeleton of the Shader as we’ve discussed earlier. Remember, it should contain all the blocks we discussed earlier.

It should now look something like this: (I’m using Notepad++ by the way, but you can use anything you’d like to.)

The basic shader skeleton

Now that we have got our skeleton done and dusted, let’s write the meat of this code!

7. First of all, modify the 1st Line i.e Shader “”. Give a name and a category to your shader, as you wish. This will make things easier in the future, when you will have a lots of shaders and categories to choose from.

So, as I have wrote:

Shader “ShaderLessons/HelloShader”

It will appear like this inside Unity, later on, as we will see shortly in the upcoming steps:

There’s our shader!

Now, let’s have our shader modify the color of our 3D object.

For that firstly, we would need a color input from the user. So, we will get the color from the user in the inspector by writing it in the Properties field as:

Whoa! What’s all this?

Now, let’s go over the line of code that we have written:

We have to change the color, so we have written a line that will let us get color input from the user, as :

_myColor(“Colour : ”, Color) = (1,1,1,1)

The syntax for the color in ShaderLab is:

name (“display name”, Color) = (number,number,number,number)

So, here in this code:

_myColor = simply the name we have assigned to the color field

(“Colour:”, Color) = this defines what will be seen in the inspector and the type of input the inspector should provide to the user.

(1,1,1,1) = These are the default values the color will be initialized as. They simply denote the RGBA color model.

It is essential that you know about the RGBA color model, before you proceed. Please click here to learn more about it and come back!

The magic of this code will be seen later on, something like this:

Presto! We now have a color picker.

So, that was it. We took the input from the user about the color, but we still have to apply it on our model. The logic for this will be written in our Subshader block. So, let’s get back to the code.

8. Inside the Subshader block, add this code. First try to figure out what each line does, with the help of the comments. I will explain it in detail shortly.

Whoa! That must have been quite a lot to take in. Take a deep breath, and proceed:

Now, let’s have a look up from the top.

The first thing that may be a bit confusing is:

#pragma surface surf Lambert

Let me dissect that, part by part :

#pragama : It is a compiler directive, which is a fancy word that means that it is a instruction to the Compiler, i.e the Unity Engine in this case. A Compiler Directive give directions to the compiler to do something, set some option, take some action, override some default, etc.

And in our case, it tells the Unity engine what type of shader we are using, what is the shader function, and what type of light is to be present on our 3d object.
 The syntax is:

#pragma surface surfaceFunction lightModel [optionalparams]

And when we compare this syntax to what we have written, we can find that:

surface : This denotes that this shader is a surface shader
surf : And the name of the function that does the surface shading is surf.
Lambert : Remember when I told that Surface shaders provide built-in lighting models, well, this tells Unity to use the Lambert model of lighting that has already been written by Unity for you!
[optionalparams] :These are some optional values to deal with the transparency and other properties, which we will be discussing later on.

Moving on, we see: =

fixed4 _myColor;

Now, If you have declared any variables before, you will know that this is the same thing.
Like:

int number;

It is an integer data type, with the variable name as number.

Similarily, fixed4 is a data type of the shader that denotes 4 values. And _myColor is the name of the color box we earlier defined in our properties section. Thus, with this line, we have made a reference to the values of the color we can get from the user. (We got them from the Properties section above, remember?)

Next up, we have:

struct Input
 {
 
 float2 uv_myTex;
 
 };

This is a structure, or struct in short. Structures are the data types that can contain different types of data in them. They are used to keep track of the different attributes of our input (the 3d model).

And, float2 is another Shader data type, which means two float(decimal values).

We have declared a uv_myTex variable, as we need the UV information of the input 3d model the UV information will tell us where to apply the texture on the 3d model .

So, inside the structure that contains all the information about the 3d model, we have used a float2 variable to hold the data of the UV points in the texture.(We shall be examining UV points and texturing in detail in the very next chapter.)

Thus, the purpose of this code was to make a place to hold all the required data of the input (the 3d model that will be affected by this shader).
We then get to this part:

void surf(Input IN, inout SurfaceOutput o)

Now, this is the function that will actually do the shading!

The function’s name is surf, and it takes in the data from the 3d model, that is stored up above on the Structure Input and then it gives as output, the rendering properties of the input 3d model. The output will tell how the model is to be shown on the screen!

The 2nd parameter is the structure, inout SurfaceOutput. SurfaceOutput is already defined by unity api. This inout struct defines the output data of the shader which is applied on the input 3d model at runtime.

Here, the SurfaceOutput (that’s already defined in the unity API)has various properties that can be used to determine how the 3d object will look like, when the shader is applied.

SurfaceOutput basically describes properties of the 3d model’s surface.
(It’s albedo color, normal, emission, specularity etc.)
 It is defined by unity like this:

struct SurfaceOutput
{
 fixed3 Albedo; // diffuse color
 fixed3 Normal; // tangent space normal, if written
 fixed3 Emission;
 half Specular; // specular power in 0..1 range
 fixed Gloss; // specular intensity
 fixed Alpha; // alpha for transparencies
};

We will just be discussing about this one now:

fixed3 Albedo;

Without going into more details, we can say that the Albedo is the base color of an object. It is the color that will be spread evenly across the surface of the object and is a fixed3 value, meaning it takes only 3 numerical values that signify its R, G and B colors.

The albedo does not handle the transparency, it is taken care of by the alpha property.

Inside the function, we find:

o.Albedo.rgb = _myColor.rgb;

This code should be pretty self explanatory now. Here, we will access the surfaceoutput structure denoted by ‘o’ in this case, and its albedo property by:

o.Albedo

And since the albedo can handle only 3 values (only the colors) as it is a fixed3, but we’ve got 4 values from the user in RGBA, so we use .rgb to indicate that we only want to load the 1st 3 values from the user supplied color i.e the RGB values.

We can use the alpha value later on when we need transparency by simply using “_myColor.a”

Thus, all this line of code is doing is to set the albedo (base color) of the 3d object’s surface to the one that we the user habe chosen through the inspector.

Lastly, we have :

FallBack “Diffuse”

In some cases, some older GPUs and integrated GPUs may not support the GPU heavy computation that goes on inside the subshader block. For those, we can use the Fallback command ,that will specify another shader to use and skip the SubShader block completely! Here, we use a Diffuse shader, that is present by default in unity.

And Boom! There you have it, your very own first shader! Not that hard now, was it?

Your shader code should be looking like this now:

Now, all that’s left is to apply it on a 3d model. Let’s do that now!

Paint it Red : Coloring our 3D Model with our brand new Shader

Whew, we now have our very own shader done and ready! Let’s do the following to apply this to a 3D object:

  1. First of all, we know that a shader is applied via a Material. An object cannot be rendered without its material. So, create a new folder named “Materials” and create a new material by going to the Project pane>Right Click>Create>Material.
  2. Then, select the material. Go on over to the Inspector and set your own shader from the Shader dropdown options present in the material.
Time for action, my dear Shader!

3. Now, we will need a 3D object to apply our shader to. For that, create a new Cube or a Sphere or you can even import your own 3D asset into the scene. I’ll be using a Cube.

4. Now, attach our Material to the object.

Attach the material which has the shader in it.

5. Then, from the color field that we had made in our shader, and which is visible now, pick a color. And voila! When you’ll be done, the result will be:

Let’s paint the town red…one cube at a time!

So, you did it! Even though it was just a very simple and a very basic shader, it took us quite some time to get it done.

But, baby steps. A surface shader today, a Vertex shader tomorrow! We will slowly build up our skills and soon start writing awesome shaders.

Now, it was quite some work. You deserve a break now. Just digest all that you’ve learned here, and try changing the colours, use other values of the output structure, and experiment a little!

Next up, we will get to know what data types are exactly used in shaders, use all the values of the output structure and also get to know UV mapping by applying a texture onto a 3D object. And we will also take a closer look at how a Surface Shader works.

Stay tuned for Part 3, Scratching the Surface : What lies within !