PDA

View Full Version : use VBO to draw dynamic text



firegun9
04-20-2007, 07:50 AM
Hi,

I am trying to use VBO to implement my Bitmap Font class. What I am trying to do is taking a single texture containing all the chars by using AngleCode Bitmap Font (http://www.angelcode.com/products/bmfont/) . Then I will generate a quad for each char from the charset with appropriate texture coordinate mapping. By the combination of these quads, I can generate bitmaps for text.

To my understanding, if I want to use VBO, the data has to be stored in arrays. In my case, I have to translate each char in the text into a quad then store the information into an array. So the quads for different text will be stored into a single array. (I can use separate arrays for different text, but that would be inefficient. Am I right??)

If I store the information for different text into a single array, I think I might have a problem fro drawing dynamic text. Like if the mouse select some object, the object's information will be shown, and the information would be gone if the object is not selected.

In that situation, the data needs to be drawn in VBO is not continuous. And it updates frequently which parts need be drawn. Is it appropriate to use VBO still?

Cyranose
04-20-2007, 12:55 PM
Nothing says that an array of GL_QUADS in a VBO need to appear in array order in screen-space, or that you need to draw everything in a VBO at once or at all. You can split up your text-quads into whatever drawing groups make sense and still use a single VBO, using multiple draw calls. And you can rewrite any parts of your VBO, as long as you re-sync the driver copy of the data afterwards (via a Map or BufferSubData). The only big gotcha is if you're using a mode that gets uncached write-combined memory (like WRITE_ONLY), you don't want to be randomly skipping around your big array with small updates. If your text doesn't change _every frame_, you might just want to mark it static and take a small hit for the actual changes from time to time.

But if all you're worried about is handling selection, then if you wind up with all of your textured-quads-as-characters in one big array, you could just keep a secondary list of those that are currently selected. You could easily redraw or predraw just those using an altered state (e.g., XOR, glow) to make them appear highlighted without ever changing the array.

Korval
04-20-2007, 03:29 PM
I can use separate arrays for different text, but that would be inefficient.Define "inefficient".

You're doing text drawing, after all. It's not like this is a particularly processor-intensive activity. It's a flat polygon with a texture on it; even a TNT would chew that sort of thing up.

Even if you were doing something like rendering, say, this web-page, with all of its text, it's still not particularly onerous for a graphics card.

As for memory efficiency, feel free to use one buffer object for each "paragraph", and divide it up for text changes (bold, italics, using multiple textures because all of your characters won't fit into one, etc). Paragraphs tend to be upwards of 40 characters, which means approximately 2.5K of VBO space (16-bytes per vertex, and 40 character * 4 verts per character). I'd just create a "cache" of buffer objects about 4K in size each, that I can "allocate" and "delete" as needed. The cache would be resizable (in case I'm doing a whole lot of text drawing at once), but it would automatically shrink itself down to some particular number of buffers (say, 8).

firegun9
04-21-2007, 12:07 AM
Thanks for you guys' replies.
Both of you suggest using single VBO.
So the code might look like this?


// binding a text line with a VBO
glBindBuffer(GL_ARRAY_BUFFER, BufferName);
glBufferData(GL_ARRAY_BUFFER, text_quads_size, NULL, GL_STREAM_DRAW);
GLvoid* text_Buffer = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(text_Buffer, text_quads_Data, text_quads_Size);
glUnmapBuffer(GL_ARRAY_BUFFER);
glVertexPointer(2, GL_FLOAT, 0, 0);
glDrawArrays(GL_QUADS, 0, VertexCount);

// binding another text line with the same VBO
glBindBuffer(GL_ARRAY_BUFFER, BufferName);
glBufferData(GL_ARRAY_BUFFER, another_text_quads_size, NULL, GL_STREAM_DRAW);
GLvoid* text_Buffer_2 = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(text_Buffer_2, another_text_quads_Data, another_text_quads_Size);
glUnmapBuffer(GL_ARRAY_BUFFER);
glVertexPointer(2, GL_FLOAT, 0, 0);
glDrawArrays(GL_QUADS, 0, VertexCount);

I have a customized structure with a color and texture coordinates for a quad, but I just ignore the color and texture coordinates attributes here.

To handle dynamic text, I can write the code above in a for-loop way. The loop checks a list of active text, which needs to be drawn. The mouse movement would update that list by selection or un-selection on objects.

Cyranose
04-21-2007, 11:40 AM
By "single VBO," I was aiming more along the lines of putting all of the text you're going to draw in one big static VBO. I get the sense Korval would recommend something similar, but maybe using N smaller VBOs instead of one big one. Hopefully, you don't need to map/memcpy/unmap every frame, but only when vertex data changes (STATIC vs. STREAM). And there's no great reason to limit yourself to only one recycled VBO like this unless you're ever low on memory.

The main point I was trying to make was that if you bound the VBO once, you could still make draw calls on whatever parts of it you wished. And there's definitely no need to send vertex data if it's unchanged from frame to frame.

In your above example, you could use multiple distinct VBOs (one per on-screen line of text, if you really want) and still avoid the data transfers when possible.

Not that it matters to most people, but optimizing text rendering *can* be important when you're using text for, say, rendering performance stats, or you simply want to minimize its impact on more performance-critical tasks.

Korval
04-21-2007, 11:48 AM
I get the sense Korval would recommend something similar, but maybe using N smaller VBOs instead of one big one.Basically, my idea is to layout a paragraph of text, then store it in a buffer object. It stays there until you're no longer going to be drawing that paragraph of text, at which time it can go away.