MeshToonMaterial shading contrast

Hi! I’m facing an issue trying to mimic the style of a game. In the image, said game is on the left and my ThreeJS renderer is on the right.

If you look at the shading on the white shinguards, you can see both systems have similar low-contrast shading. With darker colors like the brown skin tone or black leggings, however, the game has much higher-contrast shading.

This differing contrast is where I’m having trouble. My renderer has fairly constant-contrast shading no matter the color. I can darken the shadows on my renderer to better match the darker colors of the game, but this also makes the lighter colored shading far too dark.

I’ve ran into plenty of problems on this project and I can usually sort them out, but I’m truly stuck here. Any help would be greatly appreciated.

You can try to modify the final colors values with a ColorCorrectionShader shader pass. It is used in the following example that you can use as a code template:

1 Like

Thank you! That gets me good results on the shaded side. It also changes the light side which was already pretty close to my target, so I think I need to add some logic to the pass so it only works on the shaded side.

I found this function in the toon shader, getGradientIrradiance that determines the shading:

var gradientmap_pars_fragment = `
    #ifdef USE_GRADIENTMAP
        uniform sampler2D gradientMap;
    #endif
    
    vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {
        float dotNL = dot( normal, lightDirection );
        vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );

        #ifdef USE_GRADIENTMAP
            return texture2D( gradientMap, coord ).rgb;
        #else
            //                         shade side    light side
            return ( coord.x < 0.7 ) ? vec3( 0.5 ) : vec3( 1.0 );
        #endif
    }
`;

It’s used like this later in the shader:

void RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {
    vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;

I can’t figure out how to access any of this in the postprocessor though. Everything I try causes ThreeJS to crash.

Never mind, I got it by copying the ColorCorrectionShader code to the main() function of the shader. For posterity:

gl_FragColor = vec4( outgoingLight, diffuseColor.a );
vec3 ggi = getGradientIrradiance( geometry.normal, directLight.direction );
if ( ggi.x < 0.7 ) gl_FragColor.rgb = vec3(1.5,1.5,1.5) * pow( ( gl_FragColor.rgb + vec3(0,0,0) ), vec3(1.4,1.4,1.4) );

Thanks again! I need to tweak the values some more but this’ll definitely work.

3 Likes