Advertisement Jump to content
Sign in to follow this  

R&D Poor Signed Distance Font Quality When Drawn Small

This topic is 404 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

My SDF font looks great at large sizes, but not when I draw it at smaller sizes. I have my orthogonal projection matrix setup so that each unit is a 1x1 pixel. The text is rendered from Freetype2 to a texture atlas @ 56px with a spread of 8 pixels (the multiplier is 8x and scaled down). I'm drawing @ 18px in the screenshot attached to this post. The way I calculate the size of the text quads is by dividing the desired size (18px in the screenshot) by the size of the glyphs in the atlas (56px in this case), and scaling the glyph sprite by that factor. So: 18/56 = ~0.32, and I multiply the rect's size vector by that when it comes to vertex placement (this obviously doesn't apply to the vertices' texture coords). Now, I made sure that all metrics stored in my SDF font files are whole numbers (rect position/size, bearing amounts, advance, etc), but when I scale the font, vertex positions are almost always not going to be whole numbers. I increase the "edge" smoothstep shader parameter for smaller text as well, but it doesn't seem to help all that much.

Screen Shot 2017-12-11 at 9.13.20 PM.png

Share this post

Link to post
Share on other sites
1 hour ago, Hodgman said:

How do you generate the mip-maps for your SDF texture?

I forgot to post that. I'm doing basic mipmapping. Here's the member definition that creates the OpenGL texture object, and uploads the data to its memory space:

uint32_t Font::CreateTexture(size_t width, size_t height, const void* buffer)
	uint32_t handle;
	glGenTextures(1, &handle);
	glBindTexture(GL_TEXTURE_2D, handle);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, (GLsizei)width, (GLsizei)height, 0, GL_RED, GL_UNSIGNED_BYTE, buffer);

	return handle;

Here's how I call it:

texture_ = CreateTexture(atlasWidth_, atlasHeight_, (void*)((uint8_t*)buffer + offset));

Here's my text shader source as well:

static const std::string TextVertSource =
"uniform mat4 u_transMat;"
"attribute vec2 a_pos;"
"attribute vec2 a_coord;"
"varying vec2 v_coord;"
"void main()"
"	v_coord = a_coord;"
"    gl_Position = u_transMat * vec4(a_pos, 0.0, 1.0);"

static const std::string TextFragSource =
"uniform vec4 u_params;" // fillWidth, fillEdge, strokeWidth, strokeEdge
"uniform vec4 u_colors[2];" // fillColor, strokeColor
"uniform sampler2D u_tex;"
"varying vec2 v_coord;"
"void main()"
"	float distance = 1.0 - texture2D(u_tex, v_coord).r;"
"	float fillAlpha = 1.0 - smoothstep(u_params.x, u_params.x + u_params.y, distance);"
"	float strokeAlpha = 1.0 - smoothstep(u_params.z, u_params.z + u_params.w, distance);"
"	float a = fillAlpha + (1.0 - fillAlpha) * strokeAlpha;"
"	vec4 color = mix(u_colors[1], u_colors[0], fillAlpha / a);"
"	gl_FragColor = color;"
"	gl_FragColor.a = a;"


Edited by Vincent_M

Share this post

Link to post
Share on other sites

I'm not sure whether standard mipmap generation (averaging) is a good/correct one for signed distance fields (correct me if I'm wrong, it's 5:22 am and I'm in front of computer for more than 20 hours ... so I'm not really thinking straight).

Can you show how your distance fields (incl. higher pyramid levels - miplevels) look like?

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using, you agree to our community Guidelines, Terms of Use, and Privacy Policy. is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!