3
votes

I have a distance field font that I would like to render with a vertical gradient. The problem I'm having is I can't work out if there's an easy way for me to get the y coordinate relative to the glyph I'm rendering from the fonts texture atlas.

I'm using LibGDX. I can use v_texCoord.y to get the position relative to the texture. Is there an easy way I can get the y position relative to the texture region that I'm rendering for the current character? Once I've got that I can just use mix(...) to create the gradient in the fragment shader.

Vertex Shader

attribute vec4 a_position;
attribute vec2 a_texCoord0;
attribute vec4 a_color;

uniform mat4 u_projTrans;

varying vec4 v_color;
varying vec2 v_texCoord;

void main()
{
    gl_Position = u_projTrans * a_position;
    v_texCoord = a_texCoord0;
    v_color = a_color;
}

Fragment Shader

#ifdef GL_ES
  precision mediump float;
#endif

uniform sampler2D u_texture;
uniform float u_boldness;
uniform float u_smoothing;

varying vec4 v_color;
varying vec2 v_texCoord;

void main()
{
    float distance = texture2D(u_texture, v_texCoord).a;
    float alpha = smoothstep(0.5 - u_boldness - u_smoothing, 0.5 - u_boldness + u_smoothing, distance);
    gl_FragColor = vec4(v_color.rgb * alpha, alpha * v_color.a);
}
1

1 Answers

4
votes

There isn't an easy way without creating a specialize font texture. BitmapFont in libgdx is drawn with SpriteBatch, which doesn't support any vertex attributes beyond UVs, position, and color. You could try to encode vertical position relative to the TextureRegion into the color attribute, but this would take some involved reworking of the BitmapFont class. I'm not sure if you could just subclass it, or if you'd have to clone and modify it.

The easiest solution I can think of is to make a specialized font texture. For each letter in your bitmap font, you could put use a vertical red-to-green gradient, where the red and green will be replaced with other colors in the shader.

So you'd need to generate your font, and then open it in Gimp or Photoshop and add the gradient. For a standard font, create a new multiply layer and draw red-green gradients over all the letters. With a distance field font, you want to just draw the gradients in the color channels, and leave the distance field in the alpha channel.

Then in your shader, you can set uniforms for top color and bottom color that will be multiplied by the red and green colors. You can look up in other questions on SO how to use extra uniforms with SpriteBatch. Something like this in the fragment shader for a standard font:

uniform vec3 u_topColor;
uniform vec3 u_botttomColor;

vec4 textureColor = texture2D(u_texture, v_texCoord);
gl_FragColor = vec4(v_color.rgb * (u_topColor*textureColor.r + u_bottomColor*textureColor.g), textureColor.a * v_color.a);

And for a distance field font:

uniform vec3 u_topColor;
uniform vec3 u_botttomColor;

vec4 textureColor = texture2D(u_texture, v_texCoord);
float alpha = smoothstep(0.5 - u_boldness - u_smoothing, 0.5 - u_boldness + u_smoothing, textureColor.a);
gl_FragColor = vec4(v_color.rgb * (u_topColor*textureColor.r + u_bottomColor*textureColor.g), alpha * v_color.a);