Flashback '94 Shader Pack

Try the WebGL Demo!

Buy on the Asset Store!


NOW UPDATED TO VERSION 2.1.0 (6 SEPTEMBER 2016)

REQUIRES UNITY 5.1.0 OR LATER


Features

The Flashback '94 Shader Pack for Unity is a collection of shaders and scripts that emulate the graphical limitations of game consoles from the 1990s.

Affine Texture Mapping

To improve performance when rendering triangles, older graphics hardware didn't correct textures for perspective. Instead, it just distorted them in two dimensions to fit the onscreen shape of each triangle. This saved a lot of number-crunching, but it caused textures to warp when viewed at an angle, producing a telltale 'zigzag' effect on floors and walls.

Limited Vertex Precision

Although most early graphics processors used fixed-point or floating-point math (numbers with decimal places), some consoles were only able to pass integer values (whole numbers) from one component to another. This meant that each vertex in a 3D model would 'snap' to its nearest integer position instead of moving smoothly, resulting in a weird gelatinous effect as the vertices 'snapped' independently of each other.

Limited Resolution and Color

Your fancy-pants gaming PC can render 18 gorillion colors in 4K resolution at 144 frames per second, but not even the most beastly of 90s consoles could manage such epoch-defining feats of computation. Most consoles rendered 3D graphics at a very small resolution like 320x240 and upscaled it to fit a TV screen. On top of that, they had to use fewer bits to store color information, which limited color precision and caused those 'banding' artifacts that opposing tribes of brand devotees used to get into gigantic flame wars over.

Haha, isn't it great how we don't have flame wars anymore?

Instructions

When you add the Flashback '94 Shader Pack to your Unity project, you'll see some new folders in your Assets window:

  • Editor - contains a custom editor script for the post-process image effect. It runs automatically, so all you have to do is keep it in this directory.
  • Example Content - contains example content, which you may have gathered from the fact that it's named 'Example Content'.
  • Scripts - contains the image effect that can be attached to a camera in your scene.
  • Shaders - contains all the object, projector and image effect shaders you'll need to get the pleasantly hideous look you're after.

Object Shaders

If you want to apply one of these shaders to an object, first create a material, then go to its shader dropdown and select a shader from Flashback 94/Object Shader.

The possible shaders are:

  • Cubemap (Diffuse/Specular/Unlit) - shaders that apply an environment map over a diffuse texture.
  • Cutout (Diffuse/Specular/Unlit) - shaders that only draw pixels with an opacity above a cutoff value.
  • Opaque (Diffuse/Specular/Unlit) - shaders that draw solid objects with diffuse textures.
  • Rimlight (Diffuse/Specular/Unlit) - shaders that draw solid objects with diffuse textures, then move, scale and tint the back faces to create the illusion of rim lighting.
  • Transparent (Diffuse/Specular/Unlit) - shaders that draw transparent objects with diffuse textures.
  • UI Overlay - shader that draws transparent unlit UI elements that ignore depth.

All Flashback '94 object shaders use affine texture mapping, and they all have a Vertex Snapping attribute that determines how aggressively the individual vertices 'snap' when moving. The other shader attributes are named according to what they do, and behave in much the same way as Unity's built-in legacy shaders.

Projector Shaders

New in version 2.0 are two shaders for use with Unity's projector system. Because the vertex-lit rendering pipeline doesn't support real-time shadows, using a projector to create a blob shadow is the only way to ground characters and dynamic objects in the environment. Simply create a material using one of the Flashback 94/Projector shaders, and add it to the Material attribute of a Projector object.

The two shaders are:

  • Multiply (Shadows) - a shader that multiplies the color of the target surface by the color of the projected image. This is perfect for blob shadows. For best results, use a projected texture with a white background.
  • Additive (Lights) - a shader that adds the projected image color to the target surface. Great for flashlights or anything that should brighten a surface. For this one, projected textures should have a black background.

For both shaders, the projected texture should have its Texture Type attribute set to 'Advanced' and its Wrap Mode set to 'Clamp'. The mip maps that Unity automatically generates can cause visual artifacts with projectors, so either enable Border Mip Maps or just disable Generate Mip Maps entirely.

Both shaders support falloff textures, so you can use a horizontal gradient to vary the strength of the projector by distance. Intensity is based on grayscale value (white is full strength, black is off), so the color of the gradient doesn't matter. They also have a Vertex Snapping attribute, which must be the same as the target surface to prevent clipping artifacts.

Projectors in Unity can be tricky to get right, so if you want to see a correct implementation, just take a look at the Example_BlobShadow material in the Example Content/Materials directory.

Image Effect

To add the image effect to your scene, simply select your main camera, click Add Component in the inspector, then choose Flashback 94 and Post Process.

You can use the Bits Per Color Channel slider to adjust the color depth of the camera, and the Downsampling Type dropdown to choose how you want to scale the framebuffer:

  • NONE - does nothing. Use this if you just want to mess with the color bits slider.
  • RELATIVE - divides the window size by an integer and draws to a buffer of that size.
  • ABSOLUTE - draws to a buffer of the size you specify, from 32x32 to 1920x1920.

The Enable Antialiasing checkbox lets you decide whether the framebuffer should be antialiased during resizing. Switch it off for that authentic 'pixels so big you could park a cyberpunk police gunship on them' look.

Example Scene

The Example Content folder contains the source for the Flashback '94 WebGL demo. Feel free to play with the example materials to see how they work.

Best Practices

Surprisingly - even to me, and I made the friggin' thing - the shortcomings of this shader pack are similar to those of the original hardware, and require similar workarounds.

Firstly and most importantly, these shaders all began their lives as open-source Cg implementations of Unity's built-in 'VertexLit' shader. The vertex lighting model was invented by Celtic druids in 2200 BC and therefore comes with a few caveats:

  • Vertex-lit shaders will not work on modern consoles, but they will work just fine on iOS/Android and any PC/Mac/Linux computer recent enough not to use punch cards.

  • These shaders cannot cast or receive real-time shadows, as this would require per-pixel lighting. You can, however, get around this by using the included projector shaders to create blob shadows. Or, if you're a wizard, you can add your own Quake 2-style geometric projection shadows and leave a passive-aggressive review implying that I'm lazy/stupid/a communist for not including them myself.

  • They cannot be lit by more than 8 lights at a time. Your scene can have more lights in it, of course, but an object with one of these shaders applied will only receive lighting from the 8 brightest lights around it. Hey, if 8 lights were enough for Hideo Kojima, they'll be enough for you.

  • Unity does not provide light position data when rendering lightmapped objects, so if you use lightmaps with these shaders, you won't get any specular highlights.

Another thing to keep in mind is the quirky way that affine texture mapping works. If you're far enough away from an object and looking right at it, all is well. But if the camera sits directly above a single large polygon, the texture will turn into a Kubrickian slit-scan acid trip.

The way to get around this is the same as it was in the 90s: always make sure any large flat surfaces are subdivided into a grid. The sampling gets less accurate the further it gets from a vertex, so adding more vertices will ease the pain. Yes, you're technically adding more polygons where you don't need them, but if that turns out to be a performance bottleneck then you're probably developing for 90s hardware anyway.

Other workarounds include designing your textures so that any straight lines lie on polygon edges, where the distortion is least severe. You may even want to create separate geometry for critical details, which is how things like road lines were done in many racing games.

Finally, always make sure that any textures you're using with these shaders have their Filter Mode property set to 'Point'. This will ensure they have that crisp, pixelated look that graphics researchers once spent years of their lives trying to eliminate. Guess the joke's on them, huh?

Gratitude

Thanks for taking a look at the Flashback '94 Shader Pack for Unity! I hope you like it, and if you like it enough to maybe give me actual for-realsies money for it, I would appreciate that immensely.

Other people who made this possible:

  • 'Ilya.s' on the Unify Community Wiki, whose CGVertexLit shader laid the groundwork for my horrible crimes against computer graphics.
  • My former employers at Failbetter Games, who graciously allowed me to develop and sell this shader pack in my spare time.
  • My wonderful life partner Mitu, whose patience with me remains, as ever, unfathomable.

Now what are you still doing here? Go make your elders proud!