Font gradients mini-demo

Recently we changed the behaviour of the font rendering as some of you may have witnessed. We initially had a few hickups with GL errors and stuff but it seems to work pretty stable since the 1.2.140 release this week. Someone on the forum (@Pkeod I think) asked about font gradients so I thought that I could share a little tiny demo of how to achieve that effect.

To get a local [0…1] gradient in your font shader, you can do something like this:

lowp vec4  cache_sample     = texture2D(texture, var_texcoord0.xy);
lowp float gradient_value_y = fract(var_texcoord0.y / texture_size_recip.w);
lowp vec3  color_a          = vec3(1.0,0.0,1.0);
lowp vec3  color_b          = vec3(0.0,1.0,1.0);
lowp vec3  color_gradient   = mix(color_a,color_b,gradient_value_y);

gl_FragColor = vec4(color_gradient,1.0) * cache_sample.x;

Screenshot:

Demo:
https://jhonnyking.github.io/my-public-defold-examples/font-gradient/

Github:

16 Likes

Cool! Could you explain what the technical differences are of the new font rendering?

1 Like

Yeah sure I can try :slight_smile: Basically, we’ve changed the way the glyphs are placed in the font cache texture that the shaders sample from. Previously, the size of a cache cell was calculated by finding the max bounding box of the glyph set and then stored in the cache cell aligned to the top. Now instead of doing that we loop through the glyph set and use the maximum ascent and descent values and use these values as cache cell size and place the glyphs according to their ascent/descent value. Or more simply put, we place the glyphs in the cache in relation to their baseline.

Also, to calculate a relative Y value we expose two more values in the shader constant texture_size_recip in the .z and .w channels, that contain a ratio of cache cell size to cache texture width and height like this:

// This is the same as before
texture_size_recip.x = 1 / cache_texture_width;
texture_size_recip.y = 1 / cache_texture_height;
// This is new
texture_size_recip.z = cache_cell_width / cache_texture_width;
texture_size_recip.w = cache_cell_height / cache_texture_height;

This info should be added to the manual pretty soon. We have more changes with the font rendering coming up soon that deals with overlapping shadows and outline and shadow offsetting so I guess we add all of that to the manual in the same time perhaps.

Makes sense? :slight_smile:

4 Likes

Makes perfect sense! Are extra textures going to be able to be exposed to the font shaders eventually? That way we could apply repeating textures to them at runtime.

2 Likes

Something like pattern overlay like photoshop? Hm, probably not anytime soon at least. I guess you could achieve that now by a little bit of hassle via render scripts and UV trickery in the shader. Not sure how the authoring would work for such a feature in the current setup.

3 Likes

Gradient based fonts don’t display properly in GUI text nodes in editor? They show right in the font preview and in engine.

1

2 3

3 Likes

Hm, I’ll look into it, thanks for reporting!

2 Likes

Version of gradient font df fp which uses face color alpha

varying lowp vec2 var_texcoord0;
varying lowp vec4 var_face_color;
varying lowp vec4 var_outline_color;
varying lowp vec4 var_shadow_color;
varying lowp vec4 var_sdf_params;
varying lowp vec4 var_layer_mask;

uniform mediump sampler2D texture;
uniform lowp vec4 texture_size_recip;

uniform lowp vec4 color1;
uniform lowp vec4 color2;


vec3 gradient_mode()
{
    lowp float gradient_value_y = fract(var_texcoord0.y / texture_size_recip.w);
    lowp vec3  color_a          = color1.rgb;
    lowp vec3  color_b          = color2.rgb;

    return mix(color_a,color_b,gradient_value_y);
}

void main()
{
    lowp vec3 gradient_norml = gradient_mode();
    mediump vec4 sample = texture2D(texture, var_texcoord0);

    mediump float distance        = sample.x;
    mediump float distance_shadow = sample.z;

    lowp float sdf_edge      = var_sdf_params.x;
    lowp float sdf_outline   = var_sdf_params.y;
    lowp float sdf_smoothing = var_sdf_params.z;
    lowp float sdf_shadow    = var_sdf_params.w;

    // If there is no blur, the shadow should behave in the same way as the outline.
    lowp float sdf_shadow_as_outline = floor(sdf_shadow);
    // If this is a single layer font, we must make sure to not mix alpha between layers.
    lowp float sdf_is_single_layer   = var_layer_mask.a;

    lowp float face_alpha      = smoothstep(sdf_edge - sdf_smoothing, sdf_edge + sdf_smoothing, distance) * var_face_color.w;
    lowp float outline_alpha   = smoothstep(sdf_outline - sdf_smoothing, sdf_outline + sdf_smoothing, distance) * var_face_color.w;
    lowp float shadow_alpha    = smoothstep(sdf_shadow - sdf_smoothing, sdf_edge + sdf_smoothing, distance_shadow) * var_face_color.w;

    shadow_alpha = mix(shadow_alpha,outline_alpha,sdf_shadow_as_outline);

    gl_FragColor = face_alpha * vec4(gradient_norml, 1.0) * var_layer_mask.x +
    outline_alpha * var_outline_color * var_layer_mask.y * (1.0 - face_alpha * sdf_is_single_layer) +
    shadow_alpha * var_shadow_color * var_layer_mask.z * (1.0 - min(1.0,outline_alpha + face_alpha) * sdf_is_single_layer);
}

Although current df fp is a bit different this still seems to work.

Also gradient fonts seem to show up right in the editor now whenever that was fixed. :+1:

1 Like

Yeah it was fixed last week, might have flown under the radar :slight_smile:

2 Likes