1
votes
c++ - Rendering text- freetype blank screen - Stack Overflow
Asked
Viewed 2k times
1

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();
2
  • For starters, you're passing a raw, uninitialized pointer to a function (glGenTextures()) that is expecting a non-null array base pointer. Either allocate an appropriate amount of memory for you texture array or pass an automatic fixed array (in your case, likely the latter, since you are getting only one).
    – WhozCraig
    Aug 13 2013 at 15:36
  • my bad-misunderstood that function- modified appropriately, now I just get a blank screen Aug 13 2013 at 15:50
3

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.

10
  • Thank you for responding- I have a couple of questions, first in the tex image 2d call what is the significance of changing from glred to glluminance? Additionally when mine renders I get the following imgur.com/NptMgk6 my code can be seen here: pastebin.com/0M54fwpf I was going to have my program contain a map of every character, and these characters would be added either at the beginning or when first used - is this not a good way to go about it? Aug 14 2013 at 3:25
  • @user2673108 you need to put glBindTexture(GL_TEXTURE_2D, texture[0]); after GLuint texture[1]; and before you change any settings related to the texture (so just put it directly after). If you want to keep the current approach I'd recommend you add glBindTexture(GL_TEXTURE_2D, 0); and glDeleteTextures(1,texture[0];); at the end of the method. Also, you still didn't do the coordinate transformation, so your it's still upside-down.
    – PeterT
    Aug 14 2013 at 10:31
  • @user2673108 and GL_RED uploads the texture just to the red channel, so the text would display but it would be in red while GL_LUMINANCE will copy the values to all 3 channels (red, green, blue) and therefore make it appear white.
    – PeterT
    Aug 14 2013 at 10:54
  • I have done as you said, and it is working well, the only thing is I would like to remove the black fill surrounding the letters, I have tried the following fragment shader however it does not appear to work pastebin.com/ESx0cLWw Aug 14 2013 at 16:16
  • @user2673108 OpenGL is fairly low-level and alpha does nothing by default if you want to treat the color as if it were the alpha value you need to use glEnable(GL_BLEND);glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR); sometime before you draw (before glBegin()). Btw. your shader would only remove the actually black pixels, it would leave weird artifacts around the glyphs that have hinting. Also, you should try to avoid sites like pastebin when posting to stackoverflow, just add the code to your question or make a new question if you need to add a lot of code.
    – PeterT
    Aug 14 2013 at 16:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.

 
1
For starters, you're passing a raw, uninitialized pointer to a function (glGenTextures()) that is expecting a non-null array base pointer. Either allocate an appropriate amount of memory for you texture array or pass an automatic fixed array (in your case, likely the latter, since you are getting only one). - WhozCraig
my bad-misunderstood that function- modified appropriately, now I just get a blank screen - user2673108

1 Answers

3
votes

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.