Jump to content
  • Advertisement
Sign in to follow this  
Dev_NightDreamer

Signed Distance Field Text Rendering: some questions

This topic is 974 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

Hello everyone smile.png

 

I've just implemented signed distance field text rendering and I've got a few minor problems, I can't solve by myself. wacko.png

 

First of all, a quick overview of the problems:

  • I advance the cursor on the x-axis with the value of "xAdvance" (should be self-explanatory), but the space between two characters seems to be wrong (see screenshots). How can I fix this?
  • How could I set a font size like "Set this font to a size of 12px"? => How do I calculate the correct box size for a single character?
  • It occurs that the visual font thickness depends on the background color (see screenshots, in the upper one, the font seems to be clearly thicker). Why does it happen? (Due to alpha?)  And how can I prevent this behaviour?

So, here are some screenshots:

 

tQjtDDi.png

 

5YqB0wp.png

 

Now, I hope you see what I meant. If not feel free to ask and I will try to clarify it.

Here, my implementation:

 

The signed distance field font files were created with Hiero

It created the texture (512x512 in my case) and a .fnt text file.

The important (and almost all) parts (/lines) of this file are like

char id=33    x=484  y=202  width=24   height=60   xoffset=-7   yoffset=7    xadvance=33

where x,y,width,height represent non normalised texture coordinates and the offsets and xadvance represent pixel numbers.

 

The fragment shader, which calculates the actual visual character on the signed distance field texture:

#version 330 core

in vec2 _uv;

uniform vec3 color;
uniform sampler2D fontAtlas;

const float width = 0.5;
const float edge = 0.1;

void main()
{
	float distance = 1.0 - texture(fontAtlas, _uv).a;
	float alpha = 1.0 - smoothstep(width, width + edge, distance);
	gl_FragColor = vec4(color, alpha);
}

This is how I calculate the vertices for drawing:

void Font::write(const char* text, float scale, float x, float y, float z)
{
	for (const char* p = text; *p; p++)
	{

		Glyph g = m_glyphs[*p];

		float gx = x + static_cast<float>(g.xOffset) * scale;
		float gy = y - static_cast<float>(g.yOffset) * scale;
		float gw = static_cast<float>(g.width) * scale;
		float gh = static_cast<float>(g.height) * scale;

		x += g.xAdvance * scale;

		if (!gw || !gh)
			continue;

		float u1 = static_cast<float>(g.x) / static_cast<float>(m_atlas.width);
		float u2 = u1 + (static_cast<float>(g.width) / static_cast<float>(m_atlas.width));
		float v1 = static_cast<float>(m_atlas.height - g.y) / static_cast<float>(m_atlas.height);
		float v2 = v1 - (static_cast<float>(g.height) / static_cast<float>(m_atlas.height));

		m_buffer.push_back(Vertex(gx	 , gy	  , z, u1, v1));
		m_buffer.push_back(Vertex(gx	 , gy - gh, z, u1, v2));
		m_buffer.push_back(Vertex(gx + gw, gy - gh, z, u2, v2));
		m_buffer.push_back(Vertex(gx	 , gy	  , z, u1, v1));
		m_buffer.push_back(Vertex(gx + gw, gy - gh, z, u2, v2));
		m_buffer.push_back(Vertex(gx + gw, gy	  , z, u2, v1));
	}
}

This is how I render all of this:

void Font::render(const glm::mat4& ortho, const glm::vec3& color /*= glm::vec3(1.0f)*/)
{
	if (m_buffer.size() > 0)
	{
		m_shader.bind();
		m_shader.setUniform("ortho", ortho);
		m_shader.setUniform("color", color);

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, m_atlas.id);
		glBindVertexArray(m_vao);
		glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
		glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * m_buffer.size(), &m_buffer[0], GL_STREAM_DRAW);

		glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(m_buffer.size()));

		glBindTexture(GL_TEXTURE_2D, 0);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glBindVertexArray(0);
		glDisable(GL_BLEND);
			
		m_shader.unbind();
		m_buffer.clear();
	}
}

If you need some other code, just say it and I'll provide it. smile.png

 

Cheers,

NightDreamer

 

EDIT:

- minor changes in question 1 and 3

Edited by N1ghtDr34m3r

Share this post


Link to post
Share on other sites
Advertisement
  • I advance the cursor on the x-axis with the value of "xAdvance" (should be self-explanatory), but the space between two characters seems to be wrong (see screenshots). How can I fix this?

When you create signed distance field fonts, there's an extra amount of fuzziness around each character. It looks like in the hiero gui it's defined as 'spread' and you need to add an amount of padding to match the spread value. You don't mention that you're setting spread or padding in your config file, so I presume it's just taking a default value. Basically, you need to remove whatever spread/padding value that's being added from the xadvance to sort out the spacing.

 

  • It occurs that the visual font thickness depends on the background color (see screenshots, in the upper one, the font seems to be clearly thicker). Why does it happen? (Due to alpha?)  And how can I prevent this behaviour?

 

I think that might just be an optical illusion. Some sort of drop shadow (easily implemented by rendering a dark, offset version of the text before the light text), or an outline/outer glow (best implemented in the fragment shader - it's one of the strengths of SDFs) would probably sort it out.

Share this post


Link to post
Share on other sites

Hello C0lumbo,

 

thanks for replying. smile.png

 

 

 

  • I advance the cursor on the x-axis with the value of "xAdvance" (should be self-explanatory), but the space between two characters seems to be wrong (see screenshots). How can I fix this?

When you create signed distance field fonts, there's an extra amount of fuzziness around each character. It looks like in the hiero gui it's defined as 'spread' and you need to add an amount of padding to match the spread value. You don't mention that you're setting spread or padding in your config file, so I presume it's just taking a default value. Basically, you need to remove whatever spread/padding value that's being added from the xadvance to sort out the spacing.

Thanks for that. If I substract the spread from xAdvance it seems to be alright.

Anyway, sometimes it looks not so perfect, but I think, this is due to no kerning yet.

Any idea on how to implement it? I've got the data for a simple alphabet (something around 1111 kerning values), but don't know how to iterate it fast enough on a per character basis.

 

 

 

  • It occurs that the visual font thickness depends on the background color (see screenshots, in the upper one, the font seems to be clearly thicker). Why does it happen? (Due to alpha?)  And how can I prevent this behaviour?

 

I think that might just be an optical illusion. Some sort of drop shadow (easily implemented by rendering a dark, offset version of the text before the light text), or an outline/outer glow (best implemented in the fragment shader - it's one of the strengths of SDFs) would probably sort it out.

Unfortunately it is no illusion. rolleyes.gif

Here some screenshot, zoomed in a little bit:

fGeL1Z6.png?1

 

It is the same font with the same size but without a different color in the background.

Why does it occur? Is my fragment shader wrong?

 

Thanks and a good evening,

NightDreamer

Share this post


Link to post
Share on other sites

One of the gotchas with signed distance field fonts is the glyph rendering coordinate system.

 

The coordinates have to be floats.

 

Rendering a glyph at 10,10 does not produce the same results as 10.1f, 10.0f

 

Make sure your xAdvance is a float and you never truncate the coordinates to ints.

Share this post


Link to post
Share on other sites

Have you tried using alpha testing instead of alpha blending?  It looks like there is still light red around the second image (the white background), it's just less noticeable because the background is lighter.  If you're using bilinear filtering on the font, then the alpha will get softened out around the edges of the glyph, so along with the blending will produce what you're seeing.  If you use alpha testing, it will create a hard edge instead based on some absolute value.  Using point sampling would probably have the same result, but alpha testing would let you control the glyph size by increasing/decreasing the alpha cutoff (since with bilinear filtering, the edges around the glyph will blend from 0-1).

Edited by xycsoscyx

Share this post


Link to post
Share on other sites

Helloy xycsoscyx,

 

that's a point I've forgot to mention:

since I'm using OpenGL 3.3 Core, I can't use alpha testing by using glEnable(GL_ALPHA) and glAlphaFunc(...).

Is there some other way for that?

Share this post


Link to post
Share on other sites

Thanks for that, xycsoscyx.

But it occurs, that if I use alpha testing and discard it, the smooth outline of signed distance field won't be smooth anymore, due to the fact, that the smoothness (like antialiasing) relies on the alpha fading.

Any other ideas?

Edited by N1ghtDr34m3r

Share this post


Link to post
Share on other sites

Discard is the thing to use.

 

The thing to remember is the texture sampler is handling the blending for you.

 

Say you have two pixels side by side in your font. One is black and one is white.

 

When they get scaled to 4 pixels you will have 4 alpha values 0,0f.33f,0.66f,1.0f   (at a guess, depends on a lot of things, but it illustrates what I mean)

 

So when you do this in your pixel shader.

 

     if (alpha<0.5)
        discard;

 

You are doing the sharp edge you want.

Share this post


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

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!