Rich Toon Shader R&D

Inspired by Honkai Impact 3rd's latest rich toon shaders.

Rich toon shader effects can be achieved through many different ways.


Light Shade

The shading is not coming from a flattened toon shader or a flattened specular or stepped lighting.

It is coming from a partial blending which create a soft boundary between primary lighted area and self shadow color (for shadowed and self-occluded) and adding color of smooth indirect light.

You can take a look at the below picture to help what I meant by that.

Shadow Color

It can be retrieve in 2 ways for different cases.


Case A - inherited from its primary color (automatic)

We want the color to be inherited from its primary color. We can encode the primary color of the character into HSV (Hue, Saturation, Value) and shift the V (Value) of it for self shadowed areas in the shader.

hsv rgb2hsv(rgb in)
{
    hsv         out;
    double      min, max, delta;

    min = in.r < in.g ? in.r : in.g;
    min = min  < in.b ? min  : in.b;

    max = in.r > in.g ? in.r : in.g;
    max = max  > in.b ? max  : in.b;

    out.v = max;                                // v
    delta = max - min;
    if (delta < 0.00001)
    {
        out.s = 0;
        out.h = 0; // undefined, maybe nan?
        return out;
    }
    if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
        out.s = (delta / max);                  // s
    } else {
        // if max is 0, then r = g = b = 0              
        // s = 0, h is undefined
        out.s = 0.0;
        out.h = NAN;                            // its now undefined
        return out;
    }
    if( in.r >= max )                           // > is bogus, just keeps compilor happy
        out.h = ( in.g - in.b ) / delta;        // between yellow & magenta
    else
    if( in.g >= max )
        out.h = 2.0 + ( in.b - in.r ) / delta;  // between cyan & yellow
    else
        out.h = 4.0 + ( in.r - in.g ) / delta;  // between magenta & cyan

    out.h *= 60.0;                              // degrees

    if( out.h < 0.0 )
        out.h += 360.0;

    return out;
}

After V (Value) of HSV is shifted we can multiply the scene indirect light color to its shadow color using NdotL

Case B - using a texture to get the shaded color

Use a cheaper way that is basically multiplying value from ramp texture using NdotL This will multiply darker value of the texture where it is self-shadowed area so its primary color will be darken.


Face Shadow

Clean nice shadow shape on the face cannot be achieved from a normal face 3d model usually unless it is very flat model for half 2D games. It needs to do some pre-processing. The original model’s normal can be modified to be smoothed with proxy mesh like the image below. And that normal can override the original model when NdotL is calculated for face to have much more clean/fake self-shadowed area for the face. This usually requires a tech called Normal Override.


Additional ImageEffects

For over all feeling of the rich toon look and glowness for brighter part, LUT based color correction can be useful to have these well shaded toon character to the next level. Look up table (LUT) based color correction is covered in my other page (https://sites.google.com/site/semanticdatas/cgi-particle-animation/post-processing-lut-color-corrections?authuser=0)

For higher specification hardware or devices we can also try Tessellation to smooth out the shapes even more but it is recommended when the game is targeting for only higher spec devices.

For Efficiency / Optimization


Single Render Call / Color baked into Vertex Color : If this was a single render call, I’d consider three textures with the information encoded into each of the specular color (for bright spots), the self shadow color ( for shadowed and self-occluded) and the primary color for all other cases. Other case for flat shaded, you could bake it into vertex colors.


LWRP : Unity has LWRP (light weight render pipeline) this year ( still beta but if the project is longer than 6 month from now to release then its good ) which can control these render passes more deeper level based on what the look is needed.


Optimize lighting : If indirect light is something that makes the the scene heavier when there are lots of characters (at least more than 20 for example) then single pass rendering might have best chance to try which is baking the each color into vertex color. Of course, LOD would do lots of job based on level design on the environment too.


Imposter for hair or faraway NPC : For static hair efficiency, Imposter could be one of the option based on how much hair will move based on characters. (maybe NPC can have these only for optimization)


LOD for shader types : Based on distance, not only modeling can use LOD but toon shader can be divided into 3 different shader types. Most far-away characters to use cheap toon flat shader. Mid far-away characters to use toon stepping-lighting shader. Most close-up characters to have toon partial blending with indirect light color for best look.