I recently submitted my first asset to the Unity Asset Store – a set of shaders designed to produce a variety of real-time “hand-drawn” effects.
Drawing on a range of influences, including A-Ha, Murasaki Baby, and Okami, the shaders are designed to be flexible and customisable. Separate “outline” and “fill” styles can be combined in different ways to create a range of effects resembling various art styles. The following images show the result when applying some of the supplied example materials to the Unity ShadowGun sample project:
The “ballpoint pen” and “pencil” shaders both make use of a technique that calculates the apparent brightness of each part of the game scene, and replaces it with a texture interpolated from a sequence of images with progressively increasing density. This can be used to simulate artistic techniques such as hatching that represent different levels of tone intensity in an image. The basic method on which these shaders are based is described in this Microsoft Research article.
A Tonal Art Map representing increasing levels of hatching intensity
Creating a Tonal Art Map
You can use pretty much any graphics program for this – I used Paint.NET, but you could use GIMP or Photoshop just as easily.
1. Create a blank image – for performance reasons you should always make sure its dimensions are a power of 2 – 256px x 256px should be fine for most purposes.
2. Add some random noise to the image. In Paint.NET this is Effects –> Noise –> Add Noise. My TAMs are all monochrome, so set Color Saturation to 0. We’ll create the least intense image first, so set coverage relatively low (5 should do it), and set intensity high:
This should create an image as follows:
3. Now apply a blur, by Effects –> Blurs –> Motion Blur. Choose an angle and distance that looks attractive to you.
4. Notice how the effect of blurring has created the strokes we want, but lightened the stroke intensity too much in the process (in fact, you can barely see them in the image above). To correct that, we’ll use Adjustments –> Curves. Change the Luminosity curve to resemble something like this:
This will make the image look like this:
5. If you’re happy with this as a base image (if anything, the image above is actually too intense, but I’ll leave it for now), then save it as your first texture (e.g. FillTex_0.tif). Then add a new layer to the image (Layers –> Add New Layer).
NOTE: For some reason, the Paint.NET noise function doesn’t appear to work on layers with a transparent background, so the first thing to do is to use the fill tool to fill the new layer in White. This will obscure your background layer for now but don’t worry, we’ll sort that out later.
6. With the new layer selected, repeat steps 1-4 above, but rotate the angle of the motion blur by 90 degrees. This should make your top layer appear as follows:
7. Now bring up the properties for your top layer (Layer –> Layer Properties) and change the Blend Mode to Multiply:
This will cause the hatching of the second layer to be overlaid on the first, darkening it where strokes intersect in much the same manner as would occur if you were to use a pen on paper:
8. If you’re happy with that as your second texture, merge the layers down (Layers –> Merge Layers Down) and save as FillTex_1.tif.
9. Keep repeating the process of adding new layers, adding noise, blurring, changing luminosity, and multiply blending for each additional texture in your Tonal Art Map (my shader pack includes shaders for 4 textures and 6 textures). For cross-hatching, you should alternate the blur angle with each layer, but you might also want to include a bit of random variation. The important feature of this technique is that each progressively more intense texture builds upon all the preceding textures.
Of course, you don’t have to stick to simple straight lines – try different noise functions, wavy lines, or textures in your TAMs to create interesting effects, or to simulate other materials such as pencil, chalk, or charcoal. Here’s another Microsoft Research paper that demonstrates some alternative textures: