views:

7587

answers:

6

I'm trying to figure out a simple to draw some text in OpenGL. My research has shown its a fairly complex task. It involves creating (or generating at runtime) a font atlas texture, and then creating a quad for each letter with just the right placement and texture coordinates.

I've heard some good things about freetype, but have found very little about how to get it running on the iPhone, and it appears to be pretty complex.

So is there any Objective-C wrapped OpenGL text library that works with the iPhone SDK that people have been using? Or am I just over thinking this and there is an easier approach that I am missing?

+1  A: 

Check the crash landing demo. The Texture2D class that is provided allows for creation of textures with text and a font. (Hopefully there will be a better answer I would love a simpler way of drawing to an opengles view)

Nick
I think that is not quite what I'm looking for. I believe that creates texture for a whole string on the fly, which is a CPU intensive operation. Looking for more of a way to construct words out of individual quads so that I can render arbitrary text at runtime. This should be much faster.
Squeegy
Why do you think your proposed way would be quicker?
Jacob
+5  A: 
Ali Parr
This struct definitely helps me understand all the steps involved. Thank you. How would I go about generating the textures and glyph data though?
Squeegy
Thank you, This may end up being the best route. If I come up with a decent solution I am gonna try to wrap it up nice and toss it on github for all to share.
Squeegy
Superb - keep me in touch :)
Ali Parr
+12  A: 

One way to do this is by setting up a UILabel and then rendering its layer into a texture. An indirect route to this would be to first set up the UILabel with text, font, etc. and then use the following code

UIGraphicsBeginImageContext(yourLabel.frame.size);
[yourLabel.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *layerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

to capture the UILabel to a UIImage. You could then use the initWithImage: constructor of the Texture2D class in the CrashLanding project to transform this to a texture.

It looks like initWithImage: in that example re-renders the UIImage to a bitmap context and uses that to create the texture. With a little work, you could extract the relevant code from that method and alter the above code to render your layer directly into the texture.

This way, you get the nice Unicode and font support of UILabel when creating the text for your texture.

Brad Larson
I'm trying to avoid generating a different texture on the fly for every rendered string. My app would have many many different strings being displayed and it seems this would be a huge strain on the CPU and memory.
Squeegy
This is effectively what the iPhone does for every piece of text that you see. All UILabels are CALayer-backed, and CALayers are just wrapped OpenGL textures. I don't think this is as much of a resource hog as you believe.
Brad Larson
The crash landing demo application no longer seems to be available for download from the iphone developer site, is there a suitable alternative?
rck
GLSprite uses a similar technique, so I believe that you may be able to use that in its place.
Brad Larson
+2  A: 

I found a simple solution to this. Here's a quick function I wrote for it. (You will need the Texture2D class.)

- (void) drawText:(NSString*)theString AtX:(float)X Y:(float)Y {
// Use black
glColor4f(0, 0, 0, 1.0);

// Set up texture
Texture2D* statusTexture = [[Texture2D alloc] initWithString:theString dimensions:CGSizeMake(150, 150) alignment:UITextAlignmentLeft fontName:@"Helvetica" fontSize:11];

// Bind texture
glBindTexture(GL_TEXTURE_2D, [statusTexture name]);

// Enable modes needed for drawing
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

// Draw
[statusTexture drawInRect:CGRectMake(X,Y-1,1,1)]; 

// Disable modes so they don't interfere with other parts of the program
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND); 
}

I believe the input coordinates need to be in your world coordinates, but I'm not sure if that's true in general or if it just works that way for me. You might need to play with the offsets to get it right.

a mem leak with statusTexture ?
Anders K.
+1  A: 

Hi Tony,

Thanks for that method. It saved me some time.

There were a couple things I had to change though. The glBlendFunc call needed to be:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_ALPHA)

And secondly, the text appeared to be drawing into a single point. The CGRectMake call is creating a 1x1 rectangle, unless I'm reading that incorrectly.

Enjay Sea
+2  A: 

Use the "musings" font library:

http://www.themusingsofalostprogrammer.com/2010/01/how-to-do-font-rendering-in-opengl-on.html

Font font("Verdena");
font.print("Hello World!", x, y, z);

Easy :)

It comes with unicode support, loads glyphs "on-demand", and even has a way to output variables

font.print(x, y, z) << "fps: " << fps;
Dustin
That looks pretty damn sexy.
Squeegy
This library loads each character as a separate texture, which will be very slow if you're doing any decent-sized amount of drawing. It's *much* better to use a texture atlas, as suggested by some of the other answers.
Jesse Beder