Learn to write surface shaders in Unity in this complete tutorial series.
In this Unity tutorial series we will be giving you an introduction shader scripting, showing you how to write your our own surface shaders for your Unity game.
This text tutorial is accompanied by a complete video. I recommend watching the video and referring to the text as reference.
Be sure to read the tutorial carefully, and post any questions you may have if you get stuck, as there is a lot to cover. I will be using some examples from the Unity shader reference found here:
http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderExamples.html
My goal is to help you understand how shaders work and how you can write your own shaders from scratch in Unity.
NOTE: This tutorial requires very basic knowledge of scripting such as variables and functions. If you have no scripting experience I recommend you first watch our Introduction to Scripting tutorial in the getting started section.
In part 6 we added cubemap reflections to our shader, now we will boost our silhouettes by adding rim lighting
Part 7 – Adding rim lighting to our Unity surface shaders
The last part of writing surface shaders that I want to teach you before we get into writing our own lighting models, is the Rim lighting.
- Rim lighting can be used without writing a lighting model and is really easy to use.
- We are going to make use of the dot product in this shader, which allows us to get a value of 0 to 1 based on the angle.
- We can use viewDir as the input angle to get some cool results.
For this one we will be using the viewDir to have the value based on the angle we are looking from. So first let’s break down what we want to do.
- For rim lighting, we want to have some light glow around the edges of the model.
- Now viewDir is 1 when we are looking straight at it, and 0 when it is perpendicular.
- If we used this as the emission it would be bright at the center and diffuse at the rim. The opposite of what we want.
- So we will need the inverse of that.
Let’s write the shader.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
Shader "unityCookie/Rim Lighting" { Properties { _MainTex ("Diffuse Texture", 2D) = "white" {} _BumpTex ("Normal Map", 2D) = "bump" {} _RimColor ("Rim Color", Color) = (1,1,1,1) //The color tint of our rim light _RimPower ("Rim Power", Range(0.1,10)) = 3.0 //The power of our rim, note that we start from 0.1 as we don't really want 0. } Subshader { Tags { "RenderType" = "Opaque"} CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpTex; float3 viewDir; //We want to get the viewing angle }; sampler2D _MainTex; sampler2D _BumpTex; float4 _RimColor; float _RimPower; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpTex, IN.uv_BumpTex)); half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal)); // we take the view angle and place it in a 0-1 half value o.Emission = _RimColor.rgb * pow (rim, _RimPower); // we take our rim output (0-1) and take it to the power of our rim power. } ENDCG } Fallback "Diffuse" } |
Ok so there is an interesting line in there
half rim = 1.0 – saturate(dot (normalize(IN.viewDir), o.Normal));
So let’s break this up.
- saturate() – This makes sure that the value stays between -1 and 1, as we don’t want it to go outside of the valid color ranges.
- dot() – This takes the dot product of our view angle with the normal angle and returns a value from 0-1
- normalize() – This removes any values less than 0, as when the normals are facing away from you they go into negatives which we don’t normally want.
And finally we have the pow() function, which takes the first number to the power of the second number. (2 times three to the power of 4 = 2 * (3*3*3*3) )
So that’s our last shader for this series, in the next part we will take a look at debugging and optimizing our shaders.


Hi, Alex. I have learnt a lot from your shader tutorials, but will you be teaching ShaderLab language in the foreseeable future?
a strictly shaderlab code is more used for mobile development so I believe Gabriel will be covering those.
Once we have finished with CG we will move to GLSL but I can probably slot a quicky on shaderlab in somewhere
-Alex
Will you be teaching on how to write advance or non-realistic shaders? For example:
- toon-shader
- thermal vision
- texture-mixing via light ( A planet with 2 texture, day and night, and these 2 textures are mixed via lighting setup)
- texture-mixing via weight map ( A grey scale weight map is used to determine whether a specific part of a terrain should use the grass texture or rocky texture)
- et cetera
I have done all of them in Blender. I am very interest in getting the same result in Unity. I am sure that these shaders can greatly improve the production values of a game made using Unity.
Yep
I am just working through a new series on intermediate to advanced shader writing.
We go over toon shaders, texture mixing, sub surface scattering, procedural animation (jello), as well as a bunch of cool stuff that take us from these mere 50 lines of code to hundreds and even thousands of lines of glorious code!
Preview should be out before christmas, and the tutorial by new years (cross your fingers, it’s like a 25 part series)
And once that is done we will get onto some super advanced stuff like real time reflections, environment fog and post processing image effects in Unity free.
And then we will repeat the whole process in GLSL!
Excited yet?
-Alex
To be honest, I have strongly mixed feelings.
I am very excited about all the shaders you will be teaching, yet in the same time, I have concerns:
1. Will this series be a citizen-only tutorial series? (The phrase “preview” and “series” always remind me of a citizen exclusive content)
2. From your previous comments, I supposed you will be teaching GLSL basic before moving on to CG advance, after that you will redo the advance shaders in GLSL again?
3. I have no idea that post image processing can be done in Unity Free. Last time I check, post image effects (blur, glow, bloom etc) are only available in Unity Pro.
Anyhow, I am still very excited and I am looking forward to more tutorials in the foreseeable future
Looking forward to this series Alex and Citizen tuts are welcome to support my favorite site for Blender and Unity
1. The first few as we move from surface shaders to fragment shaders should be free, but we want to have a really in depth premium series to thank our citizen members.
)
Unfortunately not every tutorial I make can be free (I have to eat too
But once the entire series has finished your welcome to be citizen just for a week and grab all the tutorials.
2. Nope, We will move to CG fragment basic, then CG advanced, then GLSL basic and then GLSL advanced to keep them all together.
3. By default no, but we can apply a filter using shaders and javascript to the camera. However we cannot access the depth buffer.
I believe I have found a way to access depth in unity free but it is slow and still on paper, no testing yet.
Thanks a lot for the clarifications.
I am looking forward to more free shaders tutorials as well as the completion of the premium series. I will make sure to grab them all when the whole series is completed.
Thank you so much for these tutorials. You can count me in for the paid ones when they’re released. You managed to make shaders simple (again). I had learned HLSL shaders back in 2006 (testing in ATI’s RenderMonkey), but couldn’t quite approach the Unity stuff as easily. I was able to get just the effect I wanted in less than an hour after finishing these tutorials.
You are very welcome
The series should be starting next month where we will tear right back the the basics again to help lock in this knowledge and code awesome shaders from scratch including custom lighting models.
You can check out the demo for it here:
http://www.youtube.com/watch?v=YIjFkTltct0
-Alex