[c++] How to draw text using only OpenGL methods?

I don't have the option to use but OpenGL methods (that is glxxx() methods). I need to draw text using gl methods only. After reading the red book, I understand that it is possible only through the glBitmap() method. If this is the only possible way, then can any one help me with the pixel array information for all the characters. Is there is any other way to draw text?

This question is related to c++ c opengl graphics

The answer is


I think that the best solution for drawing text in OpenGL is texture fonts, I work with them for a long time. They are flexible, fast and nice looking (with some rear exceptions). I use special program for converting font files (.ttf for example) to texture, which is saved to file of some internal "font" format (I've developed format and program based on http://content.gpwiki.org/index.php/OpenGL:Tutorials:Font_System though my version went rather far from the original supporting Unicode and so on). When starting the main app, fonts are loaded from this "internal" format. Look link above for more information.

With such approach the main app doesn't use any special libraries like FreeType, which is undesirable for me also. Text is being drawn using standard OpenGL functions.


This article describes how to render text in OpenGL using various techniques.

With only using opengl, there are several ways:

  • using glBitmap
  • using textures
  • using display lists

Drawing text in plain OpenGL isn't a straigth-forward task. You should probably have a look at libraries for doing this (either by using a library or as an example implementation).

Some good starting points could be GLFont, OpenGL Font Survey and NeHe Tutorial for Bitmap Fonts (Windows).

Note that bitmaps are not the only way of achieving text in OpenGL as mentioned in the font survey.


Load an image with characters as texture and draw the part of that texture depending on what character you want. You can create that texture using a paint program, hardcode it or use a window component to draw to an image and retrieve that image for an exact copy of system fonts.

No need to use Glut or any other extension, just basic OpenGL operability. It gets the job done, not to mention that its been done like this for decades by professional programmers in very succesfull games and other applications.


enter image description here

Use glutStrokeCharacter(GLUT_STROKE_ROMAN, myCharString).

An example: A STAR WARS SCROLLER.

#include <windows.h>
#include <string.h>
#include <GL\glut.h>
#include <iostream.h>
#include <fstream.h>

GLfloat UpwardsScrollVelocity = -10.0;
float view=20.0;

char quote[6][80];
int numberOfQuotes=0,i;

//*********************************************
//*  glutIdleFunc(timeTick);                  *
//*********************************************

void timeTick(void)
{
    if (UpwardsScrollVelocity< -600)
        view-=0.000011;
    if(view < 0) {view=20; UpwardsScrollVelocity = -10.0;}
    //  exit(0);
    UpwardsScrollVelocity -= 0.015;
  glutPostRedisplay();

}


//*********************************************
//* printToConsoleWindow()                *
//*********************************************

void printToConsoleWindow()
{
    int l,lenghOfQuote, i;

    for(  l=0;l<numberOfQuotes;l++)
    {
        lenghOfQuote = (int)strlen(quote[l]);

        for (i = 0; i < lenghOfQuote; i++)
        {
          //cout<<quote[l][i];
        }
          //out<<endl;
    }

}

//*********************************************
//* RenderToDisplay()                       *
//*********************************************

void RenderToDisplay()
{
    int l,lenghOfQuote, i;

    glTranslatef(0.0, -100, UpwardsScrollVelocity);
    glRotatef(-20, 1.0, 0.0, 0.0);
    glScalef(0.1, 0.1, 0.1);



    for(  l=0;l<numberOfQuotes;l++)
    {
        lenghOfQuote = (int)strlen(quote[l]);
        glPushMatrix();
        glTranslatef(-(lenghOfQuote*37), -(l*200), 0.0);
        for (i = 0; i < lenghOfQuote; i++)
        {
            glColor3f((UpwardsScrollVelocity/10)+300+(l*10),(UpwardsScrollVelocity/10)+300+(l*10),0.0);
            glutStrokeCharacter(GLUT_STROKE_ROMAN, quote[l][i]);
        }
        glPopMatrix();
    }

}
//*********************************************
//* glutDisplayFunc(myDisplayFunction);       *
//*********************************************

void myDisplayFunction(void)
{
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  gluLookAt(0.0, 30.0, 100.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
  RenderToDisplay();
  glutSwapBuffers();
}
//*********************************************
//* glutReshapeFunc(reshape);               *
//*********************************************

void reshape(int w, int h)
{
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(60, 1.0, 1.0, 3200);
  glMatrixMode(GL_MODELVIEW);
}

//*********************************************
//* int main()                                *
//*********************************************


int main()
{
    strcpy(quote[0],"Luke, I am your father!.");
    strcpy(quote[1],"Obi-Wan has taught you well. ");
    strcpy(quote[2],"The force is strong with this one. ");
    strcpy(quote[3],"Alert all commands. Calculate every possible destination along their last known trajectory. ");
    strcpy(quote[4],"The force is with you, young Skywalker, but you are not a Jedi yet.");
    numberOfQuotes=5;

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 400);
    glutCreateWindow("StarWars scroller");
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glLineWidth(3);

    glutDisplayFunc(myDisplayFunction);
    glutReshapeFunc(reshape);
    glutIdleFunc(timeTick);
    glutMainLoop();

    return 0;
}

Theory

Why it is hard

Popular font formats like TrueType and OpenType are vector outline formats: they use Bezier curves to define the boundary of the letter.

Image source.

Transforming those formats into arrays of pixels (rasterization) is too specific and out of OpenGL's scope, specially because OpenGl does not have non-straight primitives (e.g. see Why is there no circle or ellipse primitive in OpenGL?)

The easiest approach is to first raster fonts ourselves on the CPU, and then give the array of pixels to OpenGL as a texture.

OpenGL then knows how to deal with arrays of pixels through textures very well.

Texture atlas

We could raster characters for every frame and re-create the textures, but that is not very efficient, specially if characters have a fixed size.

The more efficient approach is to raster all characters you plan on using and cram them on a single texture.

And then transfer that to the GPU once, and use it texture with custom uv coordinates to choose the right character.

This approach is called a texture atlas and it can be used not only for textures but also other repeatedly used textures, like tiles in a 2D game or web UI icons.

The Wikipedia picture of the full texture, which is itself taken from freetype-gl, illustrates this well:

I suspect that optimizing character placement to the smallest texture problem is an NP-hard problem, see: What algorithm can be used for packing rectangles of different sizes into the smallest rectangle possible in a fairly optimal way?

The same technique is used in web development to transmit several small images (like icons) at once, but there it is called "CSS Sprites": https://css-tricks.com/css-sprites/ and are used to hide the latency of the network instead of that of the CPU / GPU communication.

Non-CPU raster methods

There also exist methods which don't use the CPU raster to textures.

CPU rastering is simple because it uses the GPU as little as possible, but we also start thinking if it would be possible to use the GPU efficiency further.

This FOSDEM 2014 video explains other existing techniques:

Fonts inside of the 3D geometry with perspective

Rendering fonts inside of the 3D geometry with perspective (compared to an orthogonal HUD) is much more complicated, because perspective could make one part of the character much closer to the screen and larger than the other, making an uniform CPU discretization (e.g. raster, tesselation) look bad on the close part. This is actually an active research topic:

enter image description here

Distance fields are one of the popular techniques now.

Implementations

The examples that follow were all tested on Ubuntu 15.10.

Because this is a complex problem as discussed previously, most examples are large, and would blow up the 30k char limit of this answer, so just clone the respective Git repositories to compile.

They are all fully open source however, so you can just RTFS.

FreeType solutions

FreeType looks like the dominant open source font rasterization library, so it would allow us to use TrueType and OpenType fonts, making it the most elegant solution.

Examples / tutorials:

Other font rasterizers

Those seem less good than FreeType, but may be more lightweight:

Anton's OpenGL 4 Tutorials example 26 "Bitmap fonts"

The font was created by the author manually and stored in a single .png file. Letters are stored in an array form inside the image.

This method is of course not very general, and you would have difficulties with internationalization.

Build with:

make -f Makefile.linux64

Output preview:

enter image description here

opengl-tutorial chapter 11 "2D fonts"

Textures are generated from DDS files.

The tutorial explains how the DDS files were created, using CBFG and Paint.Net.

Output preview:

enter image description here

For some reason Suzanne is missing for me, but the time counter works fine: https://github.com/opengl-tutorials/ogl/issues/15

FreeGLUT

GLUT has glutStrokeCharacter and FreeGLUT is open source... https://github.com/dcnieho/FreeGLUT/blob/FG_3_0_0/src/fg_font.c#L255

OpenGLText

https://github.com/tlorach/OpenGLText

TrueType raster. By NVIDIA employee. Aims for reusability. Haven't tried it yet.

ARM Mali GLES SDK Sample

http://malideveloper.arm.com/resources/sample-code/simple-text-rendering/ seems to encode all characters on a PNG, and cut them from there.

SDL_ttf

enter image description here

Source: https://github.com/cirosantilli/cpp-cheat/blob/d36527fe4977bb9ef4b885b1ec92bd0cd3444a98/sdl/ttf.c

Lives in a separate tree to SDL, and integrates easily.

Does not provide a texture atlas implementation however, so performance will be limited: How to render fonts and text with SDL2 efficiently?

Related threads


Examples related to c++

Method Call Chaining; returning a pointer vs a reference? How can I tell if an algorithm is efficient? Difference between opening a file in binary vs text How can compare-and-swap be used for a wait-free mutual exclusion for any shared data structure? Install Qt on Ubuntu #include errors detected in vscode Cannot open include file: 'stdio.h' - Visual Studio Community 2017 - C++ Error How to fix the error "Windows SDK version 8.1" was not found? Visual Studio 2017 errors on standard headers How do I check if a Key is pressed on C++

Examples related to c

conflicting types for 'outchar' Can't compile C program on a Mac after upgrade to Mojave Program to find largest and second largest number in array Prime numbers between 1 to 100 in C Programming Language In c, in bool, true == 1 and false == 0? How I can print to stderr in C? Visual Studio Code includePath "error: assignment to expression with array type error" when I assign a struct field (C) Compiling an application for use in highly radioactive environments How can you print multiple variables inside a string using printf?

Examples related to opengl

Drawing Circle with OpenGL How do you install GLUT and OpenGL in Visual Studio 2012? How to fix this Error: #include <gl/glut.h> "Cannot open source file gl/glut.h" How to draw text using only OpenGL methods? glm rotate usage in Opengl Creating a 3D sphere in Opengl using Visual C++ How to get the GL library/headers? How to use glOrtho() in OpenGL? How to debug a GLSL shader? How to compile for Windows on Linux with gcc/g++?

Examples related to graphics

How to play or open *.mp3 or *.wav sound file in c++ program? How to use graphics.h in codeblocks? How do I get the height and width of the Android Navigation Bar programmatically? How to Change Font Size in drawString Java How can I produce an effect similar to the iOS 7 blur view? Creating a blurring overlay view Saving images in Python at a very high quality Android: Background Image Size (in Pixel) which Support All Devices How to create a Rectangle object in Java using g.fillRect method Scaling a System.Drawing.Bitmap to a given size while maintaining aspect ratio