- Unity 3D
- Shader Forge
Setup
We'll need to set up your camera to be able to render post effects. The following is a c# script that we'll attach to the rendering camera to allow it to accept post effects. Create a new unity c# script called CameraPostFX.cs, and copy/paste the following:
using UnityEngine;
using System.Collections;
using System.Collections;
[ExecuteInEditMode]
public class CameraPostFX : MonoBehaviour
{
public class CameraPostFX : MonoBehaviour
{
public Material effect;
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Graphics.Blit(source, destination, effect);
}
}
{
Graphics.Blit(source, destination, effect);
}
}
Add the CameraPostFX component to your camera.
Next, create a new material and assign it to the effect slot of the component.
Shader
The shader itself is composed of a few elements.- Full screen setup
- Blur
- High pass filter
Full screen setup
Lets start with the full screen setup. This part is set up for you when creating a new post effect in Shader Forge. Open Shader Forge in Unity by going to Window>Shader Forge. Click "New Shader" and select "Post Effect" from the options. Save your shader and the editing window will appear.This node section is set up for you. Delete the color node, as we wont be needing it, but leave the rest as is.
Blur
For the first part of the over all effect, we'll need to blur the scene a bit so we have softer lines to work with. Eventually, blurring the scene more will make your lines thicker. Here's what were doing:
![]() |
Blur theory |
To get a blur effect, we'll be sampling the screen and offsetting it in various directions, then recombining them to get a blurry effect. You can get away with less or more samples as shown above, but 8 will give us the best balance between performance and quality.
I like to use Get/Set nodes to keep my node tree clean. It lets me move blocks of features around without having to worry about connections cluttering everything up, as well as not having to zoom all the way out to make a connection on larger trees.
Lets start with making the first set nodes, so we have properties to deal with later.
Initial properties |
Create a slider with values of 0 - 0.01. This will control the distance between blur samples. Anything larger will break the effect. Attach a Set node and call it something you will remember.
Next, create a Screen Position node. Set properties for each of the output connectors, UV, U, and V. Choose "Scene UV's" from the drop down on the node. This will make the UV coordinates start at 0,0 in the lower left of the screen, and go to 1,1 in the upper right corner.
Create a Scene Color node. This will sample what is currently being rendered by the camera. Use Get nodes to create properties for "Screen Position U", "Screen Position V", and "Blur Amount" (or what ever you called the Set properties when you created them.
Add the "Screen Position U" and the "Blur Amount" properties together, then append them together to get the UV coordinates for the first sample.
At this point you can compile the shader, and assign it to the material you created earlier. Plug the Scene Color node into your custom lighting or emission slot and compile to see the effect. As a note here, if your screen is all black after compiling, you will have to hand edit the code a bit. Open the source code of the shader and delete the entire pass called "meta". This gets recreated by Shader Forge when you compile and will have to be deleted each time. When scrubbing the slider, your scene should shift right and left. Later on, we'll combine it with the more samples and an unmodified scene color to blur the scene.
This takes care of one sample, in the positive U direction. Let's take a look at creating a sample in the opposite direction.
Duplicate the nodes you just created. This is the same as the first sample, except we need to reverse the direction of the offset. Add a Remap(Simple) node in between the "Blur Amount" and Add nodes.
Set the remap values to From: 0,1, and To: 0,-1.
Now we have both directions, positive and negative in U created. To create the V directions, duplicate the nodes and add the "Blur Amount"s to the "Screen Position V" Get nodes instead. Make sure the U values are always going into the R channel input on the Append node, and the V values into the G channel.
After this we will have 4 samples to work with in each of the four directions. To make our effect better, we'll also have samples move in the four diagonal directions.
Up and to the right (towards 1,1) |
Adding the "Blur Amount" to both the U and V directions at the same time will make the scene move up and to the right towards 1,1. Using this same logic, we can extrapolate the other three diagonals.
Down and to the left (towards -1,-1) |
Up and to the left (towards -1,1) |
Down and to the right (towards 1,-1) |
That's all 8 samples. Create one more Scene Color node (for a total of 9), and add all of these Scene Color nodes together. Since we can only have 5 inputs on an Add node, you will need to add 5 of them together on one node, the other four on another, then add those two nodes together. We need to average the colors all together, so Divide the sum of the final Add node by the total number of Scene Color nodes (9). Create a Set node from that product and give it a name you will remember, we'll be using that value later.
![]() |
Complete blur section |
At this point, you can check your progress by connecting the Divide node to your custom lighting or emission input on the main node, compiling, and scrubbing back and forth on the slider in the material. You should see the scene blur. Don't worry about the right setting yet, as it will change later on.
![]() |
Blurry scene |
High pass filter
The final portion of the shader combines the blurry scene with the standard scene to extract just the high frequency details.Lets start by creating a Get node and setting it to the blurry scene you just created. Add a One Minus node to invert it, then Lerp that together with a new Scene Color node and set the T to a Value of 0.5.
Combining scene color with the inverted blur |
Desaturate the output of the Lerp to get rid of all the color information. If we plug the output of the Desaturate node into the custom lighting or emission slot, you'll see that all of our details are accented, but the scene is mostly gray. To fix that, we'll want to create a control similar to Photoshop's Level control.
![]() |
Before leveling |
Create a Remap node and plug the output of Desaturate into the value slot. To set a base line, input a Value of 0 into inMin, and a Value of 1 into inMax. This represents the current pixels on the screen and each one with a value of 0-1. To modify the range of these values, input a Value property into the outMin slot (to represent where the black value will start) and another Value property into the outMax slot (to represent where the white value will end).
Plug the output of the Remap node into a Power node with a Value property to control overall contrast, Clamp it from 0-1, then plug the output of the Clamp into your custom lighting or emission slot. That will complete your shader setup!
Image remapped |
Material
The final step to getting this tuned is to setup the material values. You'll want to spread the range from black to white out. As you are tweaking, scrub the blur slider until you find a value that works for you. The value I have set as "Sharp" will make the lines tighter or softer, and the blur property (Line Thickness in mine) will adjust the width. Here are the settings I found work for my scene:![]() |
Material settings |
Final Thoughts
![]() |
Final effect |
Although we created a simple black and white drawing here, it really is just the beginning of what you can do with it. The value input into the Main node can be used just like any other image. Try using it as a lerp mask, or a multiply or add blend. Inverting it will give you white lines on a black background. There really is a lot you can do with this effect, it's only limitation is your imagination. Enjoy!