3
\$\begingroup\$

At the moment I really want to develop a game with a similar shading technique that is used in Zelda BOTW however I do not know how I can achieve that look?

To me it looks like they are using a cel shading technique with rim lighting, however I do not know is how to design textures or use them to achieve similar results. Or even if I’ve got the right basic idea.

I would really like to know how these kind of aesthetics could be replicated and if it would be possible using a tool like Substance Designer (using a PBR workflow) or not.

\$\endgroup\$
2
  • \$\begingroup\$ We don’t consider questions asking how other games did things on-topic here, so I’ve edited your post to clarify the focus on achieving an equivalent look. \$\endgroup\$
    – user1430
    Commented Dec 22, 2017 at 0:50
  • 2
    \$\begingroup\$ Different objects in the game have different appearances, so it would help to include visual examples of the specific traits that you want in your implementation. \$\endgroup\$
    – DMGregory
    Commented Dec 22, 2017 at 5:25

1 Answer 1

10
\$\begingroup\$

The Cell Shader

To create a similar effect you will need two or three diffuse textures for the same number of brightness steps.

Use smoothstep with a small range to soften the colour band transitions.

As BotW uses different colour hues not just brightness steps for the dark and bright areas of each "colours" you need different diffuse textures. You can't simply make the colours darker/brighter.

This is where most of the style comes from.

This gives a lot of freedom allowing unlit surfaces (eyes), changing details and colours as things get brighter or darker, and other effects.

For example a different hue for the hair when dark and purple/blue dress:

Dark Shade Bright Shade

You'll also need a brightness shifting map to add textures to the different surfaces. This simulates ambient occlusion and a bit of subsurface scattering.

The texture is signed (value * 2 - 1) so areas can be made darker ( < 50% gray) or brighter ( > 50% gray) compared to normal (50% gray).

You can see the pattern for the sleeves (top left), hair (top right), and dress (bottom right).

Brightness shift

And a specular colour map to control the specular amount and its colour:

Specular

And the usual normal map (omitted for brevity).

This is the result (without post-effects such as bloom or blue filters for night time that are used extensively throughout the game.)

  • Different colours on the sleeves and the hair for the dark/bright/specular shades
  • Different brightness thresholds creating hair-strands effect and rougher dress material
  • The eyes have only a single brightness level and no specular (it's faked into the texture)

Result

Video of effect: https://www.youtube.com/watch?v=4Vhfmzl31nU

Example GLSL fragment shader code:

vec4 diff0 = texture(smp0, uv.xy); // dark "diffuse"
vec4 diff1 = texture(smp1, uv.xy); // bright "diffuse"
vec4 shift = texture(smp2, uv.xy); // brightness shift texture
vec4 specular = texture(smp3, uv.xy); // specular colour

float brightness = length(lights_diffuse_factor.rgb) + (shift.r - 0.5) * 0.3;
float specular_brightness = dot(lights_specular_factor.rgb, lights_specular_factor.rgb);

float band0 = smoothstep(settings.edges.x, settings.edges.y, brightness);

result_color =  mix(diff0, diff1, band0)
                + specular * specular_brightness;

The Environment

The environment uses an ordinary shader. The texture are painted to look more like acrylic paint brush strokes using desaturated colours.

The Post Effects

You'll need to add at least a bloom post-effect.

This is done by using a copy of the rendered frame buffer as a texture, cutting off everything below a certain threshold and interpolating the remainder back to the range 0.0 to 1.0. This can be accomplished using for example smoothstep(0.8, 1.0, color) (remaps the range [0.8, 1.0] to [0.0, 1.0], cutting off everything below 0.8)

Example (I'm using Gimp here to break down the shader steps): cut off example

Then blur the result (use a lower mip map, multiple samples, and/or multiple feedback passes, the choice is up to you, the engine's capabilities, and what the target platform handles best) and apply additively to the frame buffer:

Bloom step

The Night Effect

Then a de-saturation, colouring, and brightness curve filter can be applied to create the night effect.

De-saturation and colouring can be done with a matrix multiplication or as discrete steps:

float gray_scale = dot(colour.rgb, vec3(0.33333, 0.33333, 0.33333));
vec3 desaturated = monochrome_colour.rgb * gray_scale;
colour.rgb = mix(colour.rgb, desaturated, desaturation_factor);

The brightness curve filter can be done with a texture lookup, a Bezier curve, a fixed exponent colour.rgb *= colour.rgb, or a power function such as colour.rgb = pow(colour.rgb, exponent_factor_here).

Night effect

\$\endgroup\$
2
  • \$\begingroup\$ Wow this is a really good answer! \$\endgroup\$
    – Krupip
    Commented Dec 22, 2017 at 14:46
  • \$\begingroup\$ Is this approach definitely the same as the approach that is used for BotW? \$\endgroup\$ Commented Jan 2, 2018 at 0:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .