The most frequent compliments and questions we get are about the game’s shading. In this post I go over the technique we use, where we drew inspiration from, and hints as to where we are heading, as we continue to improve the game’s anime-inspired look.
Notice the nearly white regions. Some artists will also use black as an additional shade. (copyright Gainax)
When figuring out what visual style to use in the game, we were trying to make it fit within certain criteria:
1. The game had to scale from high to low end devices and still look good
2. Creating new content had to be fast due to our small dev team
3. If we could make it look like a 90s anime that would be great!
We tried a few different things, but nothing was looking good enough. A breakthrough happened when, in midst of this malleable moment, a friend posted this macross picture to Facebook:
A hand painted macross veritech fighter (http://www.fg-site.net/archives/3021338)
The solution was to use a custom MatCap shader. A MatCap (Material Capture) shader is a way of drawing reflective objects in 3D by encoding the reflection information from the object and from the environment into a circular image. It works best on objects with non-trivial shapes and when the camera doesn’t move too much. Its incredibly performant, versatile and looks great. I encourage anyone interested in game development to play around with it.
Check out this implementation from Jean Moreno Jeanmoreno.com
Early experiments with the technique were encouraging and we continue to improve it with each new iteration. We combine a base texture, that is painted on the model as though you were making a diffuse, with a MatCap circle to give it the lighting. The color combination per pixel can be done with an addition or multiplication. See technical details at the bottom.
Some of our early experiments
We knew MatCap easily gave objects a cartoon and cel-shaded look, but none of the off-the-shelf circles were doing what we needed. An important realization is that due to the nature of the game, the length of the ships are always in the direction of the camera, and there is most often a planet as a source of colored light. After extensive iteration we arrived at the unexpected “Pie Chart” circle (picture 4). Further work on the meshes and base textures brought out the visuals we were looking for.
Base Drone. The blue arc on the bottom of the MatCap represents the reflection from the planet below
As you can see, some of the shading is also embedded into the base texture. Each time we add a new ship to the game we give it the default MatCap circle (as seen on the Base Drone) and then make a custom one if needed or adjust the built-in shading of the base texture.
Guardian Drone. After changing the wormhole color to red, we added a matching border around the MatCap as if the light was coming from behind
If you want to be more dynamic, you can compute special MatCap edges to correspond to the light sources in different parts of the level. For example, the wormhole color was easily brought out and made to look as if coming from behind the boss by painting all edges of the circle in red. Its easy to see how to expand upon this system.
Space Worm. A custom MatCap was needed because this enemy follows rounder shapes instead of the usual protruded design
If you’re not a programmer I recommend you download Jean Moreno’s implementation from the Unity asset store (its free) and ignore the rest of this post =)
In its purest form this shader is very simple. A developer who is not a CG expert (like me) is able to meddle with it and achieve the effects unique to their game. In the vertex function you take a normal to signify a point on the surface of a sphere, and project that onto a circle. This becomes the UV coordinate on the MatCap texture, which is picked up by the fragment function. Done:
struct v2f {
float4 pos : SV_POSITION;
float2 cap : TEXCOORD0;
};
v2f vert (appdata_base v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
half2 capCoord;
capCoord.x = dot(UNITY_MATRIX_IT_MV[0].xyz,v.normal);
capCoord.y = dot(UNITY_MATRIX_IT_MV[1].xyz,v.normal);
o.cap = capCoord * 0.5 + 0.5;
return o;
}
uniform sampler2D _MatCap;
float4 frag (v2f i) : COLOR {
return tex2D(_MatCap, i.cap);
}
(CG code from Jean Moreno's free shader pack)
You can also map the circle during the fragment function, pixel by pixel. Its more expensive, but its something you’d need when, for example, combining a normal map.
In our version of the shader, we arrive at the pixel color by mixing the MatCap color with the base texture through the equation
Color = A x base x matcap + B x base + C x matcap + D
Where A, B, C and D are coefficients passed to the shader and can be specified in the material or programmatically; base is the base texture at that specific pixel and matcap is the color from the MatCap circle as defined previously.
But, forget this detail, because the key is really the MatCap circle itself. Search online and you’ll find lots of different circles to play with. I’m sure by the time we ship the game our circles will have improved a lot.
I hope this has been informative!
- Gabriel
Looks great!
wow, thats really amazing, cant wait to see how everything looks!