Do a barrel roll! in Perseus 230, a game inspired by 90s anime and foxes.

Post news Report RSS Cel-Shading With MatCap

How we implement shading in Perseus 230 to achieve the game’s distinct look.

Posted by on

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.

shader_evangelion

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:

shader_macross

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.

shader_jean_moreno

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.

Shade experiments

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 Shader

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 Shader

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.

Inquisitive Space Worm

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

Post comment Comments
GrindCrushLLC
GrindCrushLLC

Looks great!

Reply Good karma Bad karma+1 vote
Darth_Raius
Darth_Raius

wow, thats really amazing, cant wait to see how everything looks!

Reply Good karma Bad karma+1 vote
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: