Poor Signed Distance Font Quality When Drawn Small

Started by
3 comments, last by Vilem Otte 6 years, 4 months ago

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

Advertisement

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

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);
	glGenerateMipmap(GL_TEXTURE_2D);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	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;"
"}"
;

 

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?

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

This topic is closed to new replies.

Advertisement