-1

To render text with OpenGL I take the textured quad approach, I draw a quad for every character that needs to be represented. I store all the character texture information in a single texture, and use glScalef and glTranslatef on the texture matrix to select the correct character in the texture. At first I only put a few characters in the image used to create the texture and it worked well.

Since then I needed to render more characters, like lower case letters. I tried adding more characters, but now my text ends up unaligned and smaller.

Is there a proper way to create character maps, or is my approach all together wrong?

Note: I am using a mono-style font so font dimensions should not be the issue, however I would like to add support for fonts with non-uniform size characters as well.

EDIT: I'm using a vertex buffer for drawing rather than immediate mode.

EDIT 2: The texture containing the character map has 9 rows, each with 11 characters. Since the characters are the same size, I use glscalef on the texture matrix to 1/9 the width of the texture, and 1/11 the height. The VBO defines the quad (0,0),(1,0),(0,1),(1,1) and tex coords (0,0),(1,0),(0,1),(1,1). The nonalignment seems to be due to my transformations not fitting each glyph exactly. How are the optimal bounds for each glyph calculated?

Josh
  • 63
  • 6
  • 1
    Instead of glScalef glTransform (which are deprecated anyway) you should just generate the appropriate textures coordinates for each quad. – datenwolf Jul 30 '14 at 23:07
  • I'm trying to keep the required OpenGL version as low as possible, so I use those functions. I do have coordinates for characters on the character map, if that is what you are referring too, but it is based on every character being a uniform size relative to the character map size. This size is normalized by OpenGL to 1:1. – Josh Jul 30 '14 at 23:14
  • 1
    The second half of your paragraph doesn't make too much sense. As datenwolf said, set the texture coordinates of the quads (with `glTexCoord2d` or similar) instead of messing with the texture matrix. – Colonel Thirty Two Jul 31 '14 at 00:27
  • Sorry I should have mentioned that I'm using a vertex buffer for drawing rather than immediate mode, partly because it would be easy to port to OpenGL ES. – Josh Jul 31 '14 at 01:08
  • @Josh: That's still no reason, not to simply adjust the texture coordinates. Texture coordinates are not some modern OpenGL feature. They have been part of OpenGL ever since textures got introduces, which happend with OpenGL-1.0 over 20 years ago. You're surely supplying a texture coordinate array with your vertex buffers. That's where the character texture coordinates go. – datenwolf Jul 31 '14 at 08:02
  • Yes I am. Changing the texture coordinates would require changing the entire buffer. And since the vertex information has to be sequential, I would have many arrays repeating much of the same information (the quad part). Usually I just keep the VBO static, are you saying updating the VBO is better? I think the texture matrix does the same thing, but of course I may be wrong. – Josh Jul 31 '14 at 22:38
  • "I'm trying to keep the required OpenGL version as low as possible" Why? You are just making it harder for yourself with no apparent benefit. You'd be better off picking the oldest hardware you want to support, and targeting that. I highly doubt you'll find very many (or any!) of your users still have 20+ year old computers. – fintelia Aug 01 '14 at 17:29
  • I used to use a Thinkpad T60 which only supported 1.4 with arb_vertex_buffer_object, I would like to keep supporting it. Regardless VBOs are used in modern OpenGL, using shaders wouldn't change the solution. I'm asking how to compute the texture coordinates/create the char map correctly. If this is a flawed approach please let me know. – Josh Aug 01 '14 at 18:35
  • Well. Whats relevant in the end are the texcoords for each quad, no matter if you directly put them in via the texcoords or transform them with a matrix. But you have not described of what transformations you actually apply, and how your texture is organised. One can only guess that you failed to adapt your calculations to the increased texture size when you added new glyphs. But your question ins not really answerable in any useful way. – derhass Aug 01 '14 at 22:44
  • @Josh: The way you describe it, it seems like you're drawing one quad at a time (i.e. a glDrawArrays call for each single character). That's very inefficient, because the overhead for preparing the drawing is quite substancial. You should try to render at least some 10 quads to break even with the overhead. Also if you're aiming for old OpenGL profiles you can safely use client side vertex arrays. You can change their contents on the fly without having to update a VBO buffer before drawing. Client side VAs are supported until including OpenGL-3 compatibility profile. – datenwolf Aug 02 '14 at 01:30
  • I'm using glDrawElements, I don't know how I can render more than one character with a single draw call with the texture coordinates referring to the correct characters, however efficiency hasn't been an issue yet. – Josh Aug 02 '14 at 13:35

1 Answers1

0

In hopes that this may be useful to others. The optimal glyph bounds can be calculated by first normalizing the pixel offsets of each letter so that they are numbers within the range of 0 and 1. The widths and heights can also be normalized to determine the correct bounding box. If the widths and heights are uniform, like in mono fonts, static width and height values may be used for computing the glyph bounds.

Saving an array of pixel position values for each glyph would be tedious to calculate by hand, so it is better to start the first glyph at the first pixel of the character map and keep no spacing in between each letter. This would make calculating the bottom left uv coordinates easy with for loops

void GetUVs(Vector2* us, Vector2* vs, float charWidth, float charHeight, int cols, int rows)
{
    for (int x = 0; x < cols; x++)
    {
        for (int y = 0; y < rows; y++)
        {
            int index = x + cols * y;
            us[index].x = x * charWidth;
            vs[index].y = y * charHeight;
        }
    }
}

The rest of the bounds could be calculated by adding the width, the height, and the width and height respectively.

Josh
  • 63
  • 6