In my capstone class for the Game Design and Development specialization at Michigan State University I was challenged to work on characters for a mobile first person shooter game called The Target. Our client wanted us to have a lot of variety in character textures and models, but when working on the mobile platform there are a lot of considerations to be made for performance. This reminded me of an article I read a long time ago about the character shader used in . When I originally read the article a lot of it went over my head, but upon re-reading it I realized the basic concepts behind it were actually very simple. The following post will try to describe how it works and why you should use it.
To truly understand gradient mapping, you need to first understand how computers display colors. Most people working with digital images have heard the phrase “RGB” at some point. These are the primary colors of light: Red, Green, and Blue. By using combinations of these three hues you can make any other colors with projected light.
[Fig 1: RGB Channels of a Photograph]
Computers actually see images in the amount of red, blue, or green light that they need to tell the monitor to display for a given pixel. This is why every image has a red, blue, and green channel. If you isolate a single channel in photoshop you can see that it is actually a grayscale image (see Fig 1).
[Fig 2: Levels Dialog Box]
Computers have 256 shades of gray between black and white. This is why the levels adjustment layer has values from 0 to 255. (Remember we are talking computers so 0 is the first value, black, while 255 is the last value, white). Gradient mapping uses the grayscale values of an image as a base to interpolate between the colors of your given gradient. The values on the left side of the gradient are mapped to the black values in an image while the values on the right side of the gradient are mapped to the white values in the image.
[Fig 3: Gradient Map Dialog Box]
Now that we understand how digital images work, we can use the technology behind them to our advantage. In each image we can now see that there are actually three separate grayscale images that we can use. Furthermore, some image formats allow us a fourth channel for alpha/opacity (hence “RGBA”). If we start with a base image of 512×512 with an alpha channel, we really have four 512×512 images or the equivalent of one channel in a 1024×1024 pixel image.
The great thing about this method is that it only has to load one 512 image into memory, plus another smaller gradient image (5×256 pixels are all we need to cover all the values a computer can display and have a buffer for multiple gradients in a single image). From everything that I’ve read, the added cost from the shader code needed to perform the actions to specify how to use the 512 RGBA image and the gradient image are generally less than the memory used to load a full 1024 RGBA image.
Of course there are some limitations to what gradient mapping can achieve.
So we’ve decided to use a gradient mapped shader, but how do we apply it? By no means am I an expert on writing shaders. It’s something I need to spend more time doing, but the Unity code required for this type of shader is actually fairly simple. For The Target I had help from
and I have used his code as a base to write this example as well.
In order to know which channel and gradient to apply to a given group of UVs we need a way to differentiate the UVs.
We could, as in part of the L4D2 example, store all the UVs for the model in the 0-1 space and then scale various parts back up to the correct 512 size. But for this example I’m going to store each part in it’s own UV space.
You can read the entire shader by downloading the Unity Package from the
below. I’m not going to go into full detail here as there are comments in the code that explain what it is doing, more or less. Perhaps Chris will have an article explaining the finer details of that later that I can link you to.
[Fig 4: Example UVs in Blender]
I put together a simple pyramid model. It has four faces which correspond nicely to the four channels we are going to be using. Each face has it’s UVS put in a different UV space: 0-1, 1-2, 2-3, and 3-4.
[Fig 5: Separated and combined texture channels; R, G, B, A, RGB, and RGBA]
You can isolate each channel individually and draw on them inside Photoshop. It can be kind of finicky about this and because of this I ended up making several Photoshop Actions for The Target to help speed up my workflow. I recommend making a Gradient Maps, R, G, B, and Alpha folder on the top level of the file and doing all your work inside layers in those folders. Draw only in black and white and then export each folder as a new file and compile them into a new Photoshop document by copying and pasting them into each channel. You can use the Gradient Maps folder to store gradient map adjustment layers to preview the effects the engine will have (but remember to disable them before exporting).
[Fig 6: Unity Texture Settings]
In order to get things looking their best there are a few settings for the gradient texture that must be addressed in Unity. To find the material and apply it to a model go to “Custom/Gradient Map Shader” in the material drop down.
Rotate the Pyramid with the arrow keys or WASD.
| Example Files (Unity Package, 344 KB)
Below are some of the resources that helped me to wrap my head around the idea of Gradient Mapping. Hopefully my explanation was useful, but if you want to know more or to see other examples of where similar systems are used check out the links below.
| Gradient Mapping to Retouch Photos in Photoshop
| Left 4 Dead 2 Shader System
| DOTA 2 Shader Guide
| Skankerzero Modular Character System
| Gradient Mapping in UDK
| An Intro to Unity Shaders
| Guides to Writing GLSL Shaders