http://www.allegro.cc/forums/thread/599388
I would like to implement texture fonts in OpenGL ES. Is it possible to use GL_ALPHA textures? They are more space efficient than GL_RGBA. Is it possible to color the text when using GL_ALPHA textures? How? Could you send a sample code?
OpenGL ES support GL_ALPHA. It's then up to the OpenGL ES driver whether the texture you've supplied in GL_ALPHA form remains so, which will depend on whether your GPU supports GL_ALPHA. I believe that the PowerVR in the iPhone does (and can confirm that the example texture class provided by Apple uploads as GL_ALPHA when you load a suitable PNG or supply data tagged as being of that format), otherwise I'm not very certain.
Colour text using GL_ALPHA in OpenGL ES almost exactly as you would with normal OpenGL. So, for example, set the texture environment mode to 'modulate':
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
Then subsequently use calls to glColor4f (the number of combinations and permutations of glColor is reduced in OpenGL ES) to set the colour of the next string. Alternatively, supply something to glColorPointer (either directly supply data, or use the connected vertex buffer object (VBO) measures) if you want to specify colour per-vertex.
Just on the off chance that you are targetting the iPhone (my guess is that it is currently the most targetted OpenGL ES platform, but I'm fully aware that there are several other OpenGL ES platforms that are viable), note that the implementation of VBOs means that using them doesn't provide anything even close to the speed boost you'd get for switching on a conventional desktop target. There are some minor throughput increases, but definitely if you're starting out then don't worry about them now. The speed difference isn't very great, and you can worry about them later in the day — they're an extension that is easy to patch on to existing code.
EDIT:
Oh! Sample code! If you'll excuse the higgledy-piggledy blend of C++ and Objective-C:
1 | #include "Text.h" |
2 | |
3 | CRotoText *TextWriter; |
4 | Texture2D *TextSheet; |
5 | |
6 | #define NUM_GLYPHS 44 |
7 | |
8 | struct Glyph |
9 | { |
10 | CVertexArray *Vertices; |
11 | CTexCoordArray *TexCoords; |
12 | float Width; |
13 | } Glyphs[NUM_GLYPHS]; |
14 | |
15 | CRotoText::CRotoText() |
16 | { |
17 | TextSheet = [[Texture2D alloc] initWithImage:[UIImage imageNamed:@"font.png"]]; |
18 | [TextSheet Activate]; |
19 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
20 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
21 | |
22 | int Coords[NUM_GLYPHS][4] = |
23 | { |
24 | /* numbers 0 - 9 */ |
25 | {1, 14, 4, 18}, |
26 | {6, 14, 8, 18}, |
27 | {10, 14, 13, 18}, |
28 | {15, 14, 18, 18}, |
29 | {20, 14, 23, 18}, |
30 | {25, 14, 28, 18}, |
31 | {30, 14, 33, 18}, |
32 | {35, 14, 38, 18}, |
33 | {40, 14, 43, 18}, |
34 | {45, 14, 48, 18}, |
35 | |
36 | /* letters a - m */ |
37 | {1, 1, 4, 5}, |
38 | {6, 1, 9, 5}, |
39 | {11, 1, 14, 5}, |
40 | {16, 1, 19, 5}, |
41 | {21, 1, 23, 5}, |
42 | {25, 1, 27, 5}, |
43 | {29, 1, 32, 5}, |
44 | {34, 1, 37, 5}, |
45 | {40, 1, 40, 5}, |
46 | {43, 1, 46, 5}, |
47 | {48, 1, 51, 5}, |
48 | {53, 1, 55, 5}, |
49 | {57, 1, 61, 5}, |
50 | |
51 | /* letters n - z */ |
52 | {1, 7, 5, 11}, |
53 | {7, 7, 10, 11}, |
54 | {12, 7, 15, 11}, |
55 | {17, 7, 20, 12}, |
56 | {22, 7, 25, 11}, |
57 | {27, 7, 30, 11}, |
58 | {32, 7, 34, 11}, |
59 | {36, 7, 39, 11}, |
60 | {41, 7, 45, 11}, |
61 | {50, 14, 54, 18}, |
62 | {47, 7, 51, 11}, |
63 | {53, 7, 57, 11}, |
64 | {59, 7, 61, 11}, |
65 | |
66 | /* dot, semi-colon, space, exclamation mark, question mark, apostrophe */ |
67 | {56, 14, 56, 18}, |
68 | {58, 14, 58, 18}, |
69 | {1, 20, 3, 24}, |
70 | {60, 14, 60, 18}, |
71 | {58, 20, 60, 24}, |
72 | {54, 20, 54, 21}, |
73 | {50, 20, 52, 22}, |
74 | {46, 20, 47, 25}, |
75 | }; |
76 | int c = NUM_GLYPHS; |
77 | while(c--) |
78 | { |
79 | GLfloat Positions[8] = {0, 0}, TexCoords[8]; |
80 | |
81 | Positions[0] = |
82 | Positions[2] = (GLfloat)(1+Coords[c][2] - Coords[c][0]) / 64.0; |
83 | Positions[3] = |
84 | Positions[7] = 0; |
85 | |
86 | Positions[4] = |
87 | Positions[6] = 0; |
88 | Positions[1] = |
89 | Positions[5] = -(GLfloat)(1+Coords[c][3] - Coords[c][1]) / 32.0; |
90 | |
91 | TexCoords[4] = |
92 | TexCoords[6] = ((GLfloat)Coords[c][0] ) / 64.0; |
93 | TexCoords[3] = |
94 | TexCoords[7] = ((GLfloat)Coords[c][1] ) / 32.0; |
95 | |
96 | TexCoords[0] = |
97 | TexCoords[2] = ((GLfloat)Coords[c][2] + 1) / 64.0; |
98 | TexCoords[1] = |
99 | TexCoords[5] = ((GLfloat)Coords[c][3] + 1) / 32.0; |
100 | |
101 | Glyphs[c].Vertices = new CVertexArray(2, GL_FLOAT, Positions, 4); |
102 | Glyphs[c].TexCoords = new CTexCoordArray(2, GL_FLOAT, TexCoords, 4); |
103 | Glyphs[c].Width = Positions[0] + 1.0/64.0; |
104 | } |
105 | } |
106 | |
107 | CRotoText::~CRotoText() |
108 | { |
109 | [TextSheet release]; |
110 | } |
111 | |
112 | int CRotoText::GetGlyph(char t) |
113 | { |
114 | if(t >= 48 && t < 58) return t - 48; |
115 | t = toupper(t); |
116 | if(t >= 65 && t < 91) return t - 55; |
117 | |
118 | switch(t) |
119 | { |
120 | default: return -1; |
121 | case '.': return 36; |
122 | case ':': return 37; |
123 | case ' ': return 38; |
124 | case '!': return 39; |
125 | case '?': return 40; |
126 | case '/'': return 41; |
127 | case '-': return 42; |
128 | case ',': return 43; |
129 | } |
130 | } |
131 | |
132 | |
133 | void CRotoText::WriteString(char *fmt, ...) |
134 | { |
135 | char Buffer[2048]; |
136 | va_list varg; |
137 | |
138 | va_start(varg, fmt); |
139 | vsnprintf(Buffer, 2048, fmt, varg); |
140 | va_end(varg); |
141 | |
142 | char *P = Buffer; |
143 | [TextSheet Activate]; |
144 | glPushMatrix(); |
145 | glScalef(1, 0.5, 1); |
146 | while(*P) |
147 | { |
148 | int ToPrint = GetGlyph(*P); |
149 | |
150 | if(ToPrint >= 0) |
151 | { |
152 | Glyphs[ToPrint].Vertices->Enable(); |
153 | Glyphs[ToPrint].TexCoords->Enable(); |
154 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
155 | glTranslatef(Glyphs[ToPrint].Width, 0, 0); |
156 | } |
157 | P++; |
158 | } |
159 | glPopMatrix(); |
160 | } |
161 | |
162 | float CRotoText::StringWidth(char *fmt, ...) |
163 | { |
164 | char Buffer[2048]; |
165 | float Width = 0; |
166 | va_list varg; |
167 | |
168 | va_start(varg, fmt); |
169 | vsnprintf(Buffer, 2048, fmt, varg); |
170 | va_end(varg); |
171 | |
172 | char *P = Buffer; |
173 | while(*P) |
174 | { |
175 | int ToPrint = GetGlyph(*P); |
176 | |
177 | if(ToPrint >= 0) |
178 | Width += Glyphs[ToPrint].Width; |
179 | P++; |
180 | } |
181 | return Width; |
182 | } |
183 | |
184 | void CRotoText::SetScale(float scale) |
185 | { |
186 | glScalef((float)scale * (128.0/480.0), (float)scale * (128.0/480.0), 1); |
187 | } |
That code is hardcoded to the particular font bitmap I was using, as shown by the way that it doesn't even use ASCII for its internal table. CVertexArray and CTexCoordArray are my own little classes for storing stuff that will later be passed to glVertexArray and glTexCoordArray (via a VBO, but whatever), these two lines:
TextSheet = [[Texture2D alloc] initWithImage:[UIImage imageNamed:@"font.png"]]; [TextSheet Activate];
Just load and then activate a texture from the file 'font.png' using Apple's example texture class. They're Objective-C, which is why they might look slightly odd.
The text ends up being output in whatever colour was last set with glColor4f. The SetScale function is specific to the projection matrix I have set up elsewhere and the size of the font. You'll notice that each glyph is a very small number of pixels indeed (typically three or four across by five high), so I needed to be very specific about ensuring that texels map exactly to pixels, or things would look really blurry.