Question

I am using freetype, and the only thing I have left to do in order to render text is convert an ft_bitmap to something that can be rendered with opengl can someone explain how to do this? I am using glfw. With the way I have tried to do it it just gives a blank screen And here is the code that I am using:

    #include <exception>
    #include <iostream>
    #include <string>
    #include <glew.h>
    #include <GL/glfw.h>
    #include <iterator>
    #include "../include/TextRenderer.h"
    #include <ft2build.h>
    #include FT_FREETYPE_H
    #include <stdexcept>
    #include <freetype/ftglyph.h>


    using std::runtime_error;
    using std::cout;
    TextRenderer::TextRenderer(int x, int y, FT_Face Face, std::string s)
    {


        FT_Set_Char_Size(
            Face,    /* handle to face object           */
            0,       /* char_width in 1/64th of points  */
            16*64,   /* char_height in 1/64th of points */
            0,     /* horizontal device resolution    */
            0 );   /* vertical device resolution      */
        slot= Face->glyph;

        text = s;
        setsx(x);
        setsy(y);
        penX = x;
        penY = y;
        face = Face;
        //shaders
        GLuint v = glCreateShader(GL_VERTEX_SHADER) ;
        const char* vs = "void main(){  gl_Position = ftransform();}";
        glShaderSource(v,1,&vs,NULL);
        glCompileShader(v);
        GLuint f = glCreateShader(GL_FRAGMENT_SHADER) ;
        const char* fs = "uniform sampler2D texture1; void main() {    gl_FragColor = texture2D(texture1, gl_TexCoord[0].st); //And that is all we need}";
        glShaderSource(f,1,&fs,NULL);
        glCompileShader(f);
        Program= glCreateProgram();
        glAttachShader(Program,v);
        glAttachShader(Program,f);
        glLinkProgram(Program);


    }
    void TextRenderer::render()
    {
        glUseProgram(Program);
        FT_UInt  glyph_index;
        for ( int n = 0; n < text.size(); n++ )
        {
            /* retrieve glyph index from character code */

            glyph_index = FT_Get_Char_Index( face, text[n] );

                /* load glyph image into the slot (erase previous one) */
                error = FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER );

                draw(&face->glyph->bitmap,penX + slot->bitmap_left,penY - slot->bitmap_top );

                penX += *(&face->glyph->bitmap.width)+3;
                penY += slot->advance.y >> 6; /* not useful for now */
            }
        }
  void TextRenderer::draw(FT_Bitmap * bitmap,float x,float y)
{



GLuint texture [0] ;

    glGenTextures(1,texture);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RED , bitmap->width, bitmap->rows, 0, GL_RED , GL_UNSIGNED_BYTE, bitmap);


//    int loc = glGetUniformLocation(Program, "texture1");
//    glUniform1i(loc, 0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glEnable(GL_TEXTURE_2D);
        int height=bitmap->rows/10;
        int width=bitmap->width/10;
        glBegin(GL_QUADS);
        glTexCoord2f (0.0, 0.0);
        glVertex2f(x,y);
        glTexCoord2f (1.0, 0.0);
        glVertex2f(x+width,y);
        glTexCoord2f (1.0, 1.0);
       glVertex2f(x+width,y+height);
       glTexCoord2f (0.0, 1.0);
        glVertex2f(x,y+height);
       glEnd();
glDisable(GL_TEXTURE_2D);

}

What i am using to initialize text renderer:

FT_Library  library;
FT_Face     arial;
 FT_Error error = FT_Init_FreeType( &library );
    if ( error )
    {
        throw std::runtime_error("Freetype failed");
    }

    error = FT_New_Face( library,
                         "C:/Windows/Fonts/Arial.ttf",
                         0,
                         &arial );
    if ( error == FT_Err_Unknown_File_Format )
    {
        throw std::runtime_error("font format not available");
    }
    else if ( error )
    {
        throw std::runtime_error("Freetype font failed");
    }
TextRenderer t(5,10,arial,"Hello");
    t.render();
Was it helpful?

Solution

There's a lot of Problems in your program that result from not understanding what each call that you make to OpenGL or Freetype do. You should really read the documentation for the libraries instead of stacking tutorials into each other.

Let's do this one by one

Fragment Shader

const char* fs = "uniform sampler2D texture1;
                  void main() {
                      gl_FragColor = texture2D(texture1, gl_TexCoord[0].st);
                  //And that is all we need}";`

This shader doesn't compile (you should really check if it compiles with glGetShaderiv and if it links with glGetProgramiv). If you indent it correctly then you'll see that you commented out the final } because it's in the same line and after the //. So, you should remove the comment or use a \n to end the comment.

Also, for newer versions of OpenGL using gl_TexCoord is deprecated but it works if you use a compatibility profile.

Vertex Shader

just like the fragment shaders there's deprecated functionality used, namely ftransform().

But the bigger problem is that you use gl_TexCoord[0] in the fragment shader without passing it through from the vertex shader. So, you need to add the line gl_TexCoord[0]=gl_MultiTexCoord0; in your vertex shader. (As you might have guessed that is also deprecated)

Texture passing

You are passing a pointer to bitmap to glTexImage2D but bitmap is of type FT_Bitmap *, you need to pass bitmap->buffer instead.

You should not generate a new texture for each letter every frame (especially not if you're not deleting it). You should call glGentextures only once (you could put it in your TextRenderer constructor since you put all the other initialization stuff there).

Then there's the GLuint texture [0]; which should give you a compiler error. If you really need an array with one element then the syntax is GLuint texture [1];

So your final call would look something like this:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmap->width, bitmap->rows, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, bitmap->buffer);

Miscellaneous

int height=bitmap->rows/10;
int width=bitmap->width/10;

this is an integer division and if your values for bitmap->width get smaller than 10 you would get 0 as the result, which would make the quad you're trying to draw invisible (height or width of 0). If you have trouble getting the objects into view you should just translate/scale it into view. This is also deprecated but if you keep using the other stuff this would make your window have a coordinate system from [-100,-100] to [100,100] (lower-left to upper-right).

glLoadIdentity();
glScalef(0.01f, 0.01f, 1.0f);

You're also missing the coordinate conversion from FreeType to OpenGL, Freetype uses a coordinate system which starts at [0,0] in the top left corner and x is the offset to the right while y is the offset to the bottom. So if you just use these coordinates in OpenGL everything will be upside-down.

If you do all that your result should look something like this (grey background to highlight where the polygons begin and end):

glfw Window

As for your general approach, repurposing one texture and drawing letter by letter re-using and overwriting the same texture seems like an inefficient approach. It would be better to just allocate one larger texture and then use glTexSubImage2D to write the glyphs to it. If freetype re-rendering letters is a bottleneck you could also just write all the symbols you need into one texture at the beginning (for example the whole ASCII range) and then use that texture as a texture-atlas.

My general advice would also be that if you don't really want to learn OpenGL but just want to use some cross-platform rendering without bothering with the low-level stuff I'd recommend using a rendering framework instead.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top