I am looking for a way to render text efficiently. so far I’ve tried two options, both of them being neither fast nor very elegant:
Option #1 - Create a vertex buffer which contains all the needed glyphs at startup and then render each glyph individually, advancing the raster position each time:
// render all lines
for( uint i = 0; i < lines.size(); i++ )
{
// render all glyphs in a line
for( uint j = 0; j < lines[i].length(); j++ )
{
// look up glyph associated with character code
map<unsigned int, shared_ptr<Glyph>>::iterator glyphIndex = font->m_glyphs.find( lines[i][j] );
// skip if not found
if( glyphIndex == font->m_glyphs.end() )
continue;
// get the glyph itself
shared_ptr<Glyph> glyph = glyphIndex->second;
// setup view matrix from raster position
viewMatrix[3] = vec4( (vec2)rasterPosition, 0.0f, 1.0f );
// bind view matrix to shader
Engine::getGraphicsEngine()->bindConstant( m_shader, "u_View", viewMatrix );
// render glyph
Engine::getGraphicsEngine()->renderVertexbuffer( font->m_vertexbuffer, glyph->m_indexbufferOffset, 6 );
// advance raster position
rasterPosition.x += (int)((glyph->m_spacing.x)*spacing);
}
// start new line
rasterPosition.x = position.x;
rasterPosition.y -= (int)(font->m_size*spacing);
}
This is straight forward but results in a lot of draw calls.
Option #2 - Each frame, compute a glpyh’s vertices from its original vertices plus the current raster position and create and update the vertex buffer:
vector<Vertex> vertices;
vertices.reserve( text.length()*6 );
// render all lines
for( uint i = 0; i < lines.size(); i++ )
{
// render all glyphs in a line
for( uint j = 0; j < lines[i].length(); j++ )
{
// look up glyph associated with character code
map<unsigned int, shared_ptr<Glyph>>::iterator glyphIndex = font->m_glyphs.find( lines[i][j] );
// skip if not found
if( glyphIndex == font->m_glyphs.end() )
continue;
// get the glyph itself
shared_ptr<Glyph> glyph = glyphIndex->second;
for( uint i = 0; i < 6; i++ )
{
Vertex vertex = glyph->m_vertices[i];
vertex.position.x += rasterPosition.x;
vertex.position.y += rasterPosition.y;
vertices.push_back( vertex );
}
// advance raster position
rasterPosition.x += (int)((glyph->m_spacing.x)*spacing);
}
// start new line
rasterPosition.x = position.x;
rasterPosition.y -= (int)(font->m_size*spacing);
}
Engine::getGraphicsEngine()->updateVertexbuffer( font->m_vertexbuffer, vertices );
Engine::getGraphicsEngine()->renderVertexbuffer( font->m_vertexbuffer );
Option #2 seems to make more sense since it requires a lot less draw calls but I don’t like the idea of having to re-create and re-upload the vertex buffer every time the text changes.
The only thing that has to be changed in between glyphs is the raster position. Let’s say I store a glyph’s horizontal and vertical spacing in the zw components of its vertices. Would there be way to update a global variable in the vertex shader which keeps track of the advancing raster position?
I’ll try to illustrate what I want to do. So let’s say the input vertex stores its position in its xy components and the glyph’s spacing in the xw components. If I had a global variable to store the current raster position in the vertex shader, I could position the glyphs like this:
vec2 rasterPosition; // <- a global variable that is incremented with each vertex
void main()
{
...
// advance raster position by glyph's spacing
rasterPosition += gl_Vertex.zw;
// final position is vertex position + raster position
vec2 Vertex = gl_Vertex.xy+rasterPosition;
// multiply with modelview and projection matrix and output vertex
gl_Position = u_ModelviewProjectionMatrix * vec4( Vertex, 0.0, 1.0 );
}
Is that possible in OpenGL? A uniform variable won’t work as it’s read only…
Thank you for your input!