Sign in to follow this  
rene_g

[RESOLVED] Direct3D 9: Bitmap font artifacts

Recommended Posts

IDE: MSVC++ 2005 I'm trying to create my own GUI, and recently just implemented a simple bitmap font renderer. However, for some reason, my texts are not rendered consistently. Here is a sample: The first one (Starting at the top) is rendered correctly. But all the others are rendered incorrectly. You can fx. see that the body of the letter 'T' is thinner in at least 2 of the other texts. And it seems a row a pixels is cut off of the top of all the letters in text 3 (Still from the top). Basically, my question is: What could be the reason for my texts being rendered like this? I know there's probably hundreds of things that could be the reason, so let me explain what I've found out so far. My texts are consisting of quads, where each letter is a quad. These quads are then textured with a texture that contains all characters, generated by AngelCode's Bitmap Font Generator. Parsing the font file and generating the vertices works as it should. The vertices are created starting from (0,0,0) and while creating the vertices, I do no transformations whatsoever. This is confirmed by PIX, because when I debug the text's, their vertices have the exact same values, both coordinates, color and UV coordinates. So the meshes of the individual texts are the same, there's no difference. They have the same coordinates, colors and UV coordinates. Right before I draw the texts, I translate them into position by calling Device->SetTransform(D3DTS_WORLD, &position). (Just as a side note, I don't use any scaling anywhere in my code, so that can't be the problem either) Here is the code that draws a text:
		m_device->SetStreamSource(0, m_vertexBuffer, 0, sizeof(CharVertex));
		m_device->SetFVF(CharVertex::FVF);
		if (m_fontTexture)
			m_device->SetTexture(0, m_fontTexture);
		m_device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
		m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
		m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
		DWORD min, mag, mip;
		m_device->GetSamplerState(0, D3DSAMP_MINFILTER, &min);
		m_device->GetSamplerState(0, D3DSAMP_MAGFILTER, &mag);
		m_device->GetSamplerState(0, D3DSAMP_MIPFILTER, &mip);
		m_device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		m_device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		m_device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

		m_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2 * text.length()); // I use 2 primitives per char. Number of primitives = number of chars * 2

		m_device->SetSamplerState(0, D3DSAMP_MINFILTER, min);
		m_device->SetSamplerState(0, D3DSAMP_MAGFILTER, mag);
		m_device->SetSamplerState(0, D3DSAMP_MIPFILTER, mip);

		m_device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);






In PIX, I've looked at the letter 'T' in 2 different texts. One that renders correctly (The top one) and one that doesn't. In the Pre-Vertex Shader state, both chars have exactly the same values. In the Post-Vertex Shader state, the positions are obviously different, as they are rendered different places on the screen, but the colors and UV coordinates are the same. So it seems to me, that it can't be a problem in the creation of the quads for the texts. But what can it be then? If I move my texts around on the screen, they are rendered the same way the whole time, meaning they don't change from "Rendered correctly" to "Rendered incorrectly". So at least, the error is consistent. Regarding the first text, which is rendered correctly in the above image, if I offset the position of the text by just 0.001f, the text is rendered incorrectly, with the same symptoms as the other ones on the image above. I offset the text by just adding the 0.001f to the line where I calculate the transform matrix, like this:
			D3DXMATRIX translation, transform;
			D3DXMatrixTranslation(&translation, 0.0f, m_window.GetHeight(), 0.0f); // The first text is rendered correctly
			//D3DXMatrixTranslation(&translation, 0.001f, m_window.GetHeight(), 0.0f); // The first text is rendered incorrectly
			D3DXMatrixMultiply(&transform, &m_window.GetTransformMatrix(), &translation);
			m_device->SetTransform(D3DTS_WORLD, &transform);






The same things happens if I offset the text in Y. This makes absolutely no sense at all to me. If I don't offset, the text is rendered correctly, even when I move it around on the screen! But if I offset it by just a tiny bit, the text is rendered incorrectly, also when I move the text around on the screen. How can that be!? The transform matrix is the same if I offset the position or don't offset but move the text while my app is running. I've spent so much trying to figure this one out, but I've run out of ideas of what it can be. If anyone has any suggestions on what it could be, or maybe just suggestions to what I can try and look for in PIX to locate the problem, I would really appreciate it! Thanks alot guys! PS: I don't know if it's helpful or not for you, but you can download my PIX run and see for yourself. It's here (3,43 mb) [Edited by - rene_g on May 16, 2008 10:25:12 AM]

Share this post


Link to post
Share on other sites
From a quick glance it seems that you`re not using pre-transformed vertices into screen space. If that is the case, you can`t have the quad to have correct dimensions in pixels.

Generally, to have per-pixel-perfect HUD text, you need to make sure, that
1. You`re using pretransformed vertices into screenspace so you can easily check if each quad has correct dimensions. Otherwise they can get stretched and this is causing visual disturbances

2. Does all of your charaters start vertically AND horizontally at the same pixel offset within their quad inside the texture ? Check in some 2D SW package by turning on the 2D grid and spend 5 minutes checking few letters whether they REALLY start at 6th texel from the start of the character`s texture space (16x16,32x32,...).

3. Are the values of length of each character correct and actually coherent with the offset (as per point 2) ? I assume you`re using characters of variable width.

4. Half-texel offset. Not sure about DX9, but in DX8 the coordinates need to be offseted for half of a texel - check with DX docs.

5. Point / Linear Filtering - this also affects the actual initial offset - play with it a little bit. But, in any case, use POINT filtering to avoid blurred HUD images.

Share this post


Link to post
Share on other sites
You've only given information on what you do the same for each render. What is different between the dissimilar examples? Is it the position?

The problem can't be found by looking at only the code that works in some unknown case and not in some other unknown case.

Share this post


Link to post
Share on other sites
>VladR
1. Yes, that's correct, I don't use pre-transformed vertices. You might be right, but then again, how come my text is rendered correctly ALL time, even when I move it around (Maybe I should explain what I mean with this. My text is rendered relatively to the window that contains the text. I can move these windows around with my mouse) So, even when I move the window around and my text is moved with it, it's consistently rendered correct. But if I offset the position from the window by just a tiny bit, it's rendered incorrectly. If the problem was a rounding error or similiar, I suppose the text would change between being rendered correctly and incorrectly depending on the position I move it to with the window. But as I said, that's not the case.

2. I've tested and each letter in the quads have the exact same dimensions across the text's. If you compare text 1 and 3, it looks like the individual chars have been moved 1 pixel up, and a row of darker pixels have been inserted at the bottom. And that's strange, because the coordinates of the quads are the same, and so is the UV coordinates.

3. Yes, the lengths of each char is the same across the different texts.

4. You're right, and I already do this when I create the vertices and the UV coordinates.

5. I'll try messing around with some other filtering, thanks!

Quote:
Original post by Buckeye
You've only given information on what you do the same for each render. What is different between the dissimilar examples? Is it the position?


I'm sorry I didn't make that clear. Yes, the only thing that is different is the position.

[Edited by - rene_g on May 15, 2008 10:07:16 AM]

Share this post


Link to post
Share on other sites
The U offsets for "e" and "s" character are obviously incorrect and are off for 1 texel. Check by debugging why is that happening.

Share this post


Link to post
Share on other sites
I agree, it looks like the UV coordinates are off by 1. But in PIX, the UV coordinates are the same as for the 'e' and 's' that's rendered correctly. And I also tried to break at the line where I calculate the U and V coordinates for the upper left corner of the 'e' and 's' quad. The UV coordinates are the same for those that renders correctly and incorrectly. :-(
Here is a screenshot from PIX where you can see the individual UV coordinates for both a text that renders correctly (On the right side of the screenshot) and one that renderes incorrectly (On the left side of the screenshot).

Share this post


Link to post
Share on other sites
Problem solved.

I'm a n00b. Just as VladR suggested, the problem was the classical "Directly mapping texels to pixels". I believed I was correctly compensating for this, but it turned out I had misunderstood what I should do to remedy the problem. I thought I had to subtract 0.5 from the UV coordinates on my quads. However, that does not solve the problem. I now understand that I should subtract (Or add) 0.5 to the POSITION of the quad. When I changed this in my implementation, my font renderer worked as it should.

Furthermore, as VladR also suggested, point filtering is a better choice for bitmap fonts, as linear filtering produces some slight visual artifacts.

Thanks alot for your help VladR! :-)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this