In my engine, I use a dynamic vertex buffer for batching up text data (each character == 1 vertex) and do point-to-quad expansion in geometry shader.
As far as I know, glVertex*
, glNormal*
, glTexCoord*
are considered obsolete (like display lists) and are not recommended to use.
This gives a comprehensive overview of various OpenGL-based text rendering techniques:
The details of the technique I'm using:
Offline:
0) prepare a font texture atlas (with tightly packed glyphs, block-compressed) and remember glyph data (position, size, unicode => glyph index mapping);
for these purposes I use MakeSpriteFont from DirectX Tool Kit.
At launch time:
- load the font texture and glyph data (submit data of each mipmap level to
glCompressedTexSubImage2D
);
- create a dynamic vertex buffer (
GL_DYNAMIC_DRAW
) for holding MAX_TEXT_LENGTH
vertices;
At draw time:
4) parse the submitted text string and update the VBO using the glyph data;
5) draw the points from VBO
glBindBuffer( GL_ARRAY_BUFFER, dynamicVBO_id );
glDrawArrays( GL_POINTS, 0/*first vertex*/, vertexCount/*==character count*/ );
My vertex shader code:
#version 420 core
in vec4 a_texCoord0; // .xy - center position, .zw - width and height
in vec4 a_texCoord1; // st coords for top left and bottom right corners
out VSO {
vec4 xy_wh;
vec4 tl_br;
} outputs;
void main()
{
// gl_Position will be written by geometry shader
outputs.xy_wh = a_texCoord0;
outputs.tl_br = a_texCoord1;
}
Geometry shader code:
#version 420 core
layout(points) in;
layout(triangle_strip, max_vertices=4) out;
in VSO {
vec4 xy_wh;
vec4 tl_br; // UVs for top left and bottom right corners
} inputs[];
out GSO {
vec2 texCoord;
} outputs;
void main()
{
vec2 pos = inputs[0].xy_wh.xy;
float width = inputs[0].xy_wh.z;
float height = inputs[0].xy_wh.w;
vec2 tl = inputs[0].tl_br.xy;
vec2 br = inputs[0].tl_br.zw;
gl_Position = vec4( pos.x, pos.y, 0.0f, 1.0f );
outputs.texCoord = vec2( tl.x, tl.y );
EmitVertex();
gl_Position = vec4( pos.x + width, pos.y, 0.0f, 1.0f );
outputs.texCoord = vec2( br.x, tl.y );
EmitVertex();
gl_Position = vec4( pos.x, pos.y - height, 0.0f, 1.0f );
outputs.texCoord = vec2( tl.x, br.y );
EmitVertex();
gl_Position = vec4( pos.x + width, pos.y - height, 0.0f, 1.0f );
outputs.texCoord = vec2( br.x, br.y );
EmitVertex();
EndPrimitive();
}
Fragment shader code:
#version 420 core
in GSO {
vec2 texCoord;
} inputs;
out vec4 o_pixelColor;
uniform sampler2D s_font;
void main()
{
vec4 textureColor = texture( s_font, inputs.texCoord ).rgba;
if( textureColor.w < 1.0/255.0 ) {
discard;
}
o_pixelColor = textureColor;
}
I'm not in any way proficient in OpenGL.