You are on page 1of 18

Content 1.

Introduction
Introduction 1
Getting Started 2 In this tutorial we will create a Unity3d
Surface shader which tries to achieve a
Implementing the lightingmodell
cartoony look beyond simply using a ramp
How lighting works 4 texture in the lighting function.
Implementation of our own Lighting Model 6
Getting the UV to the Lightingfunction 9 Instead I will try to create the look by
Finally Shading 10 determing the shading of the area
Some improvements to the lighting 12 between bright and dark areas with a
Finalizing the Lighting 13 greyscale mask. Thats what the main part
of this tutorial will be about. Later on we
The rim-lighting will add a rim-effect to further improve the
visual appeal. I will also try to explain the
Explanation 15 concepts behind the things we will be
Implementing 16 doing.

Sources 17

Introduction - Cartoon Shader Tutorial 1


1. Getting Started step 3 Place the ImprodLowPoly-model in the
Setting Up the Project scene. (Drag & Drop from the project view)

step 4 Create a new Shader.


step 1 Create a new Project Assets > Create > Shader
Name it CartoonShader.
step 2 Import the AssetPackage you can download here.
https://dl.dropboxusercontent.com/u/28326381/shader%20dnload%20package.unit step 5 Create a new Material.
ypackage
Assets > Create > Material
Name it CartoonMaterial.
step 6 Apply the Cartoon Material to the model
youve placed in the scene in step 3.

step 7 Create a direction Light.


GameObject > Create Other > Directional Light
You can adjust the light-angle by rotating the light.

Fig1.: How to import a custom package to unity

Getting Started - Cartoon Shader Tutorial 2


step 9 Open the shader by double
Clicking the
CartoonShader in your
project view. Monodevelop
should open. Give it a few
seconds and you should see
something like in this
image.
This is our shader code.

step 10 Assign the CartoonShader


to the CartoonMaterial.
Your Unityproject should look about like this right now. You
Select the
should have: CartoonMaterial in the
Imported the downloaded Assets project view and hit the
Created the shader and the material and applied the material to the button next to shader
model and select
Created a directional light CartoonShader from
the Custom section.

Getting Started - Cartoon Shader Tutorial 3


2. Lighting Model How lighting works
Lets begin with the most important part of this shader the shading. Since the What is a surface?
goal of this shader is to mix the diffuse texture with greyscale textures based on What we are basically trying to do is, coloring and shading
the lighting, we will write our own lightingfunction and use it in our shader. surfaces.
A surface always has direction it is facing to. This direction is
How the Surface Shader works stored as a vector the surface normal. A 3d mesh in our
Since we use a surface shader, we calculate the actual color of the surface in the rendering pipeline consists of many triangles each with its
void surf (Input IN, inout SurfaceOutput o) function. This function gets called own normal (Fig 2).
for each surface fragment the engine is rendering. The function recieves Fig2.: surface normals of a mesh
important data via the IN struct. We take this data und use it, to calculate the What is a light?
colors, normals, and anything else needed in the SurfaceOutPut-struct which gets In terms of rendering the key elements forming a light are:
passed on the next step, the lighting function. Here the data can be changed and The position / direction of the light Source
used to let each light in the scene, modify our rendering fragment. The color
This, off course, is only a very simplified explanation of a surface shader in Thats basically it. And thats all we need to compute our
Unity3d. lighting.

Read More about how a Surfaceshader works here:


http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaders.html

Lighting Model - Cartoon Shader Tutorial 4


How lighting is computed
The shading of a surface is the result of a calculation between the incoming light direction
and the surface normal. Different lighting models calculate this differently. But most models We then use this value to
resemble the way lighting works in real life. multiply it with our surface
color (red in fig.3). The more
The formula is easy the surface normals direction
The more a surface is facing towards the light direction, the brighter it will points towards the light
appear. direction, the higher the
lightValue gets thus increasing
the amount of surfaceColor.

Or more mathematically speaking:


The bigger the angle between the surface normal and the light
direction gets, the less lightcolor is applied to the already calculated
surface color.

By calculating the dot product of two vectors we get the angle between two vectors as a
scalar between 0 and 1. While dot(v1, v2) returns 1 when both vectors are facing towards
each other.

Lighting Model - Cartoon Shader Tutorial 5


This part of the code, should look like this right now:
2. Implementation of our own Lighting Model
Shader "Custom/CartoonShader" {
step 1 Declaration of our own Lighting Model Properties {
Open the editor with the shader code we have opened previously. (or just _MainTex ("Base (RGB)", 2D) = "white" {}
double-click the shader again). }
SubShader {
Tags { "RenderType"="Opaque" }
We can either use a built-in lighting model from unity like Lambert, Phong
LOD 200
etc. but we also have the option to write our own little lighting function.
CGPROGRAM
In order to do that, we simply declare a new function after the Input Code- #pragma surface surf Lambert
Block. By putting this code after the Input Struct-declaration:
sampler2D _MainTex;
half4 LightingCartoon(SurfaceOutput s, half3 dir, half attend){ struct Input {
float2 uv_MainTex;
} };

Half4 means that this function will return a four-component vector (which will be used as half4 LightingCartoon(SurfaceOutput s, half3 dir, half attend){
color (Red,Green,Blue,Alpha)
}
When naming the function LightingCartoon we tell Unity that this function may be used
to compute lighting void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
SurfaceOutput provides all the data previously calculated in the surf function o.Albedo = c.rgb;
o.Alpha = c.a;
Dir is the light direction }
ENDCG
}
FallBack "Diffuse"
}

Lighting Model- Cartoon Shader Tutorial 6


step 2 Lets return some stuff The LightingFunction should now like this:
Since this function returns a color, we should write some code in order to test our own
lighting model. half4 LightingCartoon(SurfaceOutput s, half3 dir, half attend){
Our final lighting model will be based on a so called Wrap-Lambert which only lets a
portion of the light affect the surface color. dir = normalize(dir);

half NdotL = saturate( dot (s.Normal, dir));


In our in Step1 declared LightingCartoon function we put the following :
half4 c;
c.rgb = ((NdotL * 0.2f)+0.8f) * s.Albedo * _LightColor0;
c.a = s.Alpha;
return c;
dir = normalize(dir);
This will normalize our light-direction vector leaving it with a }
magnitude of 1.0. Otherwise this will mess up the dot-product
half NdotL = saturate( dot (s.Normal, dir));
Calculate the dot product. Saturate(float) makes sure, the value is clamped
between 0 and 1 because we dont want to multiply our surface colors with
negative values) step 2.1
Now we need to tell unity to use our lightingmodel we do this in the pragma section.
half4 c; The default lightingmodel is the lambert lighting model. So we will change that by
c.rgb = ((NdotL * 0.2f)+0.8f) * s.Albedo * _LightColor0; changing lambert > Cartoon.
c.a = s.Alpha;
#pragma surface surf Cartoon
return c;

Actually return a color, first we declar it.Then the Albedo (diffuse) Color
from the surface shader gets multiplied with our light-angle. And finally we
let the Lightcolor have a shot to modify or final color. _LightColor0 is a
global value, describing the currently rendered lightcolor

Lighting Model - Cartoon Shader Tutorial 7


Save the shader, and switch back to the unity3d Editor, the shader will now compile and the material in
the viewport will update as soon as its done giving you result looking like this:
step 4 Declaring some properties
What we are trying to achieve is having a a shading color for the bright and for the dark side. And a texture
for the transition zone.
Notice how even the dark areas arent Lets start with the hardest part, getting the zones right.
totally black thats because of this
piece of code we have just written: _BrightColor("Bright Color", Color) = (1,0,0)
First we declare new properties for
the two colors. In our properties _Threshold1("Threshold Bright to Dark", range(0,1)) = 0.2
(NdotL * 0.2f)+0.8f) _DarkColor("Dark Color", Color) = (0,1,0)
codeblock we put this code:
_Threshold2("Threshold Middle to Dark", range(0,1)) = 0.9
Making sure, there is at least 20% _TransitionTexture("Transition Texture", 2D) = "white" {}
lighting.
Then we need to add the respective sampler2D _TransitionTexture;
Additionally we have still some fair shader variables to get the half4 _BrightColor;
amount of Ambient light in the scene. properties values to the shader. Add half4 _DarkColor;
Which we will turn of now this before the INPUT struct. half _Threshold1;
half _Threshold2;

Just a reminder this is our


plan:

step 3 Turning off the ambient light


Go to Edit > Render Settings and

Assign black to the Ambient Light Color-Slot

Lighting Model - Cartoon Shader Tutorial 8


struct SurfaceOutputCustom { step 5.1
fixed3 Albedo;
Save the file. Go to the editor and check the material.
fixed3 Normal; Getting our struct to work
The properties of the material on your object should
fixed3 Emission;
look like this (fig. 4) This struct actually contains the usual data plus
half Specular;
fixed Gloss; the UV part. We will write the UV to the struct
Unfortunately these colors dont affect our shader
much at the moment. fixed Alpha; which will then get passed to the lighting
Lets change that! fixed viewFallof; function. We need to change the arguments of
half2 UV; the lighting and surface function to make this
};
happen.

Change the lighting function declaration now to this


half4 LightingCartoon(SurfaceOutputCustom s, half3 dir, half attend)

And we will change the surface function to this:


void surf (Input IN, inout SurfaceOutputCustom o)

Also add this line to the surf function this will write the UV values to our struct so
we can access is it in the lighting.
step 5 Getting the UV to the Lightingfunction
We do not have access to the uv-coordinates of the current rendered surface piece in o.UV = IN.uv_MainTex;
the lighting function. We will code our own struct which gets passed on through all the
functions and use this one instead of the SurfaceOutput struct. Lets declare the
SurfaceOutputCustom struct right after the Input-struct declaration.

Lighting Models - Cartoon Shader Tutorial 9


step 6 Finally Shading!
lerp(DarkColor, BrightColor, tex2D(_TransitionTexture, s.UV) )
First I have to introduce ternary expressions. You are probably familiar with the if-else structure in
conditional statements. Although the shader code is able to handle if-else structures in this case we
need to express our conditions with a ternary operation.
It works like this:
Lerp(value1, value2, percent); tex2D(texture, UV);
Interpolates between Color1 and Color2 We will get the percent value from our
using the percent value transitionTexture @ the current UV
position

Add this line to the LightingCartoon funcion, right after the NdotL declaration.

We will use the treshold to determine which color for the light based shading we will use.
half3 ShadowColor = NdotL < _Threshold1 ? _DarkColor : NdotL < _Threshold2 ?
lerp(_DarkColor, _BrightColor, tex2D(_TransitionTexture, s.UV)) : _BrightColor;

and multiply our previously calculated ShadowColor to the output color by changing it to this:

c.rgb = ((NdotL * 0.2f)+0.8f) * s.Albedo * _LightColor0 * ShadowColor;

In code it looks like this (Ive left out the texturing for _BrightColor and _DarkColor for
now)
ShadowColor = NdotL < _Threshold1 ? _DarkColor : NdotL < _Threshold2 ? lerp(_DarkColor,
_BrightColor, tex2D(_TransitionTexture, s.UV)) : _BrightColor; Lighting Model - Cartoon Shader Tutorial 10
Shader "Custom/CartoonShader" {
Step 6 |CODE Lets wrap it. Properties { c.a = s.Alpha;
_MainTex ("Base (RGB)", 2D) = "white" {} return c;
_BrightColor("Bright Color", Color) = (1,0,0)
The code should look like this at the moment. _Threshold1("Threshold Bright to Dark", range(0,1)) = 0.2 }
_DarkColor("Dark Color", Color) = (0,1,0)
_Threshold2("Threshold Middle to Dark", range(0,1)) = 0.9 void surf (Input IN, inout SurfaceOutputCustom o) {
_TransitionTexture("Transition Texture", 2D) = "white" {} half4 c = tex2D (_MainTex, IN.uv_MainTex);
Assign tex1 to the transition texture slot in the material which should } o.UV = IN.uv_MainTex;
produce something like this: SubShader {
o.Albedo = c;
o.Alpha = c.a;
Tags { "RenderType"="Opaque" } }
LOD 200 ENDCG
}
CGPROGRAM FallBack "Diffuse"
#pragma surface surf Cartoon }

sampler2D _MainTex;
sampler2D _TransitionTexture;
half4 _BrightColor;
half4 _DarkColor;
half _Threshold1;
half _Threshold2;

struct Input {
float2 uv_MainTex;
};

struct SurfaceOutputCustom{
fixed3 Albedo;
fixed3 Normal;
fixed3 Emission;
half Specular;
fixed Gloss;
fixed Alpha;
fixed viewFallof;
half2 UV;
};

half4 LightingCartoon(SurfaceOutputCustom s, half3 dir, half attend){

dir = normalize(dir);
half NdotL = saturate( dot (s.Normal, dir));
half3 ShadowColor = NdotL < _Threshold1 ? _DarkColor : NdotL <
_Threshold2 ? lerp(_DarkColor, _BrightColor, tex2D(_TransitionTexture, s.UV)) :
_BrightColor;

half4 c;
c.rgb = ((NdotL * 0.4f)+0.6f) * s.Albedo * _LightColor0 * ShadowColor;

Lighting Model - Cartoon Shader Tutorial 11


Step 7 Some improvements to the lighting.
Lets add the Shader variables as well. Right before the Input half _DarkTextureSize;
struct, after the shader variable from step 7. half _BrightTextureSize;
We definitely need to be able to influence the transitiontextures size.
half _BrightTextureIntensity;
Since I dont want to add a variable to our custom struct for each UV, we will simply
add a float and scale the UV accordingly. sampler2D _BrightTexture;
Add this property right after the existing ones: sampler2D _DarkTexture;
_TransitionTextureSize("Transition Texture Size", range(0.1, 50)) = 1

And the shader variable.. Just after half Treshhold1 Whenever we look up the _BrightColor or _DarkColor. We will multiply it
with a value from its texture. We Also want to set the intensity of the
half _TransitionTextureSize;
_BrightTexture. We need to change a lot of stuff in our LightingCartoon
function. Between the NdotL and _Shadowcolor declaration!
Now we will use this value to scale the transition texture. By changing the texture ADD
half4 darkColor = _DarkColor * tex2D(_DarkTexture, s.UV * _DarkTextureSize);
lookup in our ternary expresion half4 brightColor = _BrightColor * ( tex2D(_BrightTexture, s.UV * _BrightTextureSize) * _BrightTextureIntensity +
half3 ShadowColor = NdotL < _Threshold1 ? _DarkColor : NdotL < _Threshold2 ? (1-_BrightTextureIntensity));
lerp(_DarkColor, _BrightColor, tex2D(_TransitionTexture, s.UV *_TransitionTextureSize)) :
_BrightColor; With these lines we declare a new Color-Vector and multiply the Colors from the properties with the values from the
texture at that spot. While the darkColor is a simple multiplication. We craft in the _BrightTextureIntensity as a sort of
Now we will use this value to scale the transition texture. By changing the texture weight for the TextureValue of the brightColor.
lookup in our ternary expression. Now we only need to add the new colors to the ternary expression.

half3 ShadowColor = NdotL < _Threshold1 ? darkColor : NdotL < _Threshold2 ? lerp(darkColor,
brightColor, tex2D(_TransitionTexture, s.UV * _TransitionTextureSize)) : brightColor;
Step 7.1 Adding Textures
To have more artistic control over the shading to make the colors look painted for
example, we are going to multiply the shades with a texture. Again, first the
properties. Add those:

_BrightTexture("Bright Color Texture", 2D) = "white"{}


_BrightTextureSize("Bright Texture Size", range(0.1,50)) = 1
_BrightTextureIntensity("Bright Texture Intensity", range(0.0,1)) = 0.5
_DarkTexture("Dark Color Texture", 2D) = "white"{}
_DarkTextureSize("Dark Texture Size", range(0.1,50)) = 1
Lighting Model - Cartoon Shader Tutorial 12
Step 7.2 Finalizing the Lighting

Add some textures


for the different
shading zones, and
play around with the
colors, and texture
sizes. Settings used
for this Screenshot:

BrightColor:
(255,215,178)

BrightTexture:
Pencil_stroke_02

DarkColor::
(99,79,66)

DarkTexture:
Pencil_stroke_01

TransitionTexture:
Pencil_strokes_03

Lighting Model - Cartoon Shader Tutorial 13


CODE
Shader "Custom/CartoonShader" { half4 c;
Properties { sampler2D _BrightTexture;
_MainTex ("Base (RGB)", 2D) = "white" {} sampler2D _DarkTexture; c.rgb = ((NdotL * 0.4f)+0.6f) * s.Albedo * _LightColor0 *
_BrightColor("Bright Color", Color) = (1,0,0) ShadowColor;
_Threshold1("Threshold Bright to Dark", range(0,1)) = 0.2 struct Input {
_DarkColor("Dark Color", Color) = (0,1,0) float2 uv_MainTex; c.a = s.Alpha;
_Threshold2("Threshold Middle to Dark", range(0,1)) = 0.9 }; return c;
_TransitionTexture("Transition Texture", 2D) = "white" {}
_TransitionTextureSize("Transition Texture Size", range(0.1, 50)) = 1 struct SurfaceOutputCustom { }
fixed3 Albedo;
_BrightTexture("Bright Color Texture", 2D) = "white"{} fixed3 Normal; void surf (Input IN, inout SurfaceOutputCustom o) {
_BrightTextureSize("Bright Texture Size", range(0.1,50)) = 1 fixed3 Emission; half4 c = tex2D (_MainTex, IN.uv_MainTex);
_BrightTextureIntensity("Bright Texture Intensity", range(0.0,1)) = half Specular; o.UV = IN.uv_MainTex;
0.5 fixed Gloss;
fixed Alpha; o.Albedo = c;
_DarkTexture("Dark Color Texture", 2D) = "white"{} fixed viewFallof; o.Alpha = c.a;
_DarkTextureSize("Dark Texture Size", range(0.1,50)) = 1 half2 UV; }
} ENDCG
}; }
SubShader { FallBack "Diffuse"
Tags { "RenderType"="Opaque" } half4 LightingCartoon(SurfaceOutputCustom s, half3 dir, half }
LOD 200 attend){

CGPROGRAM dir = normalize(dir);


#pragma surface surf Cartoon
half NdotL = saturate( dot (s.Normal, dir));
sampler2D _MainTex;
sampler2D _TransitionTexture; half4 darkColor = _DarkColor * tex2D(_DarkTexture, s.UV *
half4 _BrightColor; _DarkTextureSize);
half4 _DarkColor; half4 brightColor = _BrightColor * ( tex2D(_BrightTexture, s.UV *
half _Threshold1; _BrightTextureSize) * _BrightTextureIntensity + (1-
half _Threshold2; _BrightTextureIntensity));
half _TransitionTextureSize;
half3 ShadowColor = NdotL < _Threshold1 ? darkColor : NdotL <
half _DarkTextureSize; _Threshold2 ? lerp(darkColor, brightColor, tex2D(_TransitionTexture,
half _BrightTextureSize; s.UV * _TransitionTextureSize)) : brightColor;
half _BrightTextureIntensity;

Lighting Model - Cartoon Shader Tutorial 14


3. Rim Effect Step 1 Properties
Declare these properties in the properties block.
To improve the visual appeal of the shader we are going to add some Rim-Lighting.
We need some properties first.
Polygons facing away from the Camera are going to get tinted. Usually this technique is
_RimColor("Rim Color", Color) = (1,0,0)
used to simulate backlighting. But we will hijack the effect for our own purpose. Lets
meet an old friend. The dot product. This time our scalar will reflect the angle between
_RimColor _RimPower("Rim Position", range(0,3)) = 1
This will be the color the surface is going to get _RimStrength("Rim Strength", range(0,1)) = 1
the view Direction and the surface normal. The more a surface is facing away from the
be tinted with.
viewer the smaller the dot product will become.
_RimPower And those in our shader code (Bevor the Input struct):
As usual we will calculate the dot product. We then
multiply a Color, declared for this purpose, with this The value we are using in pow to flatten our half4 _RimColor;
value and finally we will output this color in the curve. half _RimPower;
emission color of the surface. half _RimStrength;
_RimStrength
In a later step we will modify the Albedo color based How much of the calculated Color is going to be
on the rim lighting so we can even use the rim-effect applied
to darken the rims.

Powering the dot product!


We will modify the scalar with a pow() function to
take control over the dispersion. Step 2 Getting the view direction
When looking at a We have to tell Unity to provide a vector containing the view direction first.
sphere, the dot product To do that, we simply modify the input struct. Add float3 viewDir to the Input struct.
looks like this. From
inner to outer From now on, unity will write struct Input {
polygons. the viewdirection to the Input float2 uv_MainTex;
struct so we can use it. float3 viewDir;
};
There is even more data you can
add to your Input. Check out the
We can modify this
reference:
curve using the pow
http://docs.unity3d.com/Docum
function
entation/Components/SL-
SurfaceShaders.html
Rim Effect- Cartoon Shader Tutorial 15
Step 3 Lets Rim Step 4 Test
Save the shader and go to the
With the viewdirection ready to use in our Input data, we can start adding code to the editor. Hopefully it looks
Surf function. something like this.
This is quite much the effect I
half NdotView = 1 - dot(normalize(IN.viewDir), o.Normal); wanted to achieve. Although I
would like to do a small
The first step is to get the dot product between the view direction and the rendered improvment.
piece. Right now, we actually cannot do
Its very important to normalize the viewDir, because it actually reflects a vector FROM a rim effect which darkens the
the rendered piece TO the camera. So probably this vectors magnitude is quite long, or model. Thats because we are
short. However to calculate a reasonable dot product we need both vectors to have applying the color to the emission
a magnitude of 1. Thats exaclty what normalize() does. channel. Which by nature gets
multiplied with the color.
The 1-dotProduct kind of inverts the value. Meaning: The more the Normal is
pointing away from the viewDir the larger our value gets.
Facing Away = dot product = 0 Step 5 Messing with the Albedo
Facing Towards = dot product = 1
The way well be working around
The rim-effect should INCREASE when the faces are facing away. So we need an this, will be to reduce the albedo o.Albedo = c * ( 1 - NdotView * _RimStrength);
Increasing value. strength on faces because the
Emission will illuminate these
areays anyway. Change the
assignment of the Albedo Color
NdotView = pow(NdotView, _RimPower); In the surf function) to this.
According to the last page we power-the resulting value to determine the size of the
rim-effect.

o.Emission = NdotView * _RimColor * _RimStrength;


According to the last page we power-the resulting value to determine the size of the
rim-effect.

Rim Effect - Cartoon Shader Tutorial 16


FINAL CODE
Shader "Custom/CartoonShader" { half _DarkTextureSize; _BrightTextureIntensity));
Properties { half _BrightTextureSize;
_MainTex ("Base (RGB)", 2D) = "white" {} half _BrightTextureIntensity; half3 ShadowColor = NdotL < _Threshold1 ? darkColor : NdotL <
_BrightColor("Bright Color", Color) = (1,0,0) _Threshold2 ? lerp(darkColor, brightColor, tex2D(_TransitionTexture, s.UV *
_Threshold1("Threshold Bright to Dark", range(0,1)) = 0.2 sampler2D _BrightTexture; _TransitionTextureSize)) : brightColor;
_DarkColor("Dark Color", Color) = (0,1,0) sampler2D _DarkTexture;
_Threshold2("Threshold Middle to Dark", range(0,1)) = 0.9 half4 c;
_TransitionTexture("Transition Texture", 2D) = "white" {} half4 _RimColor;
_TransitionTextureSize("Transition Texture Size", range(0.1, 50)) = 1 half _RimPower; c.rgb = ((NdotL * 0.4f)+0.6f) * s.Albedo * _LightColor0 *
half _RimStrength; ShadowColor;
_BrightTexture("Bright Color Texture", 2D) = "white"{}
_BrightTextureSize("Bright Texture Size", range(0.1,50)) = 1 struct Input { c.a = s.Alpha;
_BrightTextureIntensity("Bright Texture Intensity", range(0.0,1)) = 0.5 float2 uv_MainTex; return c;
float3 viewDir;
_DarkTexture("Dark Color Texture", 2D) = "white"{} }; }
_DarkTextureSize("Dark Texture Size", range(0.1,50)) = 1
struct SurfaceOutputCustom { void surf (Input IN, inout SurfaceOutputCustom o) {
_RimColor("Rim Color", Color) = (1,0,0) fixed3 Albedo; half4 c = tex2D (_MainTex, IN.uv_MainTex);
_RimPower("Rim Position", range(0,4)) = 2 fixed3 Normal; o.UV = IN.uv_MainTex;
_RimStrength("Rim Strength", range(0,1)) = 1 fixed3 Emission;
half Specular; half NdotView = 1 - dot(normalize(IN.viewDir), o.Normal);
} fixed Gloss;
fixed Alpha; NdotView = pow(NdotView, _RimPower);
SubShader { fixed viewFallof;
Tags { "RenderType"="Opaque" } half2 UV;
LOD 200 o.Emission = NdotView * _RimColor * _RimStrength;
};
CGPROGRAM
#pragma surface surf Cartoon half4 LightingCartoon(SurfaceOutputCustom s, half3 dir, half attend){
o.Albedo = c * ( 1 - NdotView * _RimStrength);
sampler2D _MainTex; dir = normalize(dir); o.Alpha = c.a;
sampler2D _TransitionTexture; }
half4 _BrightColor; half NdotL = saturate( dot (s.Normal, dir)); ENDCG
half4 _DarkColor; }
half _Threshold1; half4 darkColor = _DarkColor * tex2D(_DarkTexture, s.UV * FallBack "Diffuse"
half _Threshold2; _DarkTextureSize); }
half _TransitionTextureSize; half4 brightColor = _BrightColor * ( tex2D(_BrightTexture, s.UV *
_BrightTextureSize) * _BrightTextureIntensity + (1-
Sources

DOWNLOADS
Tutorial Package the stuff you need to start with this tuorial
https://dl.dropboxusercontent.com/u/28326381/shader%20download%20package.unitypackage

Finished Tutorial Package everything is already done


https://dl.dropboxusercontent.com/u/28326381/shader%20tutorial%20final.unitypackage
Links:

The maker of the awesome model were using through the tutorial:
http://www.parkparkin.com/

Sources - Cartoon Shader Tutorial 18

You might also like