Rendering Text with FreeType doesn't work

Hi!

I recently tried to render text using FreeType, I followed a tutorial findable at the website of learnopengl and even though the code seems to be OK it’s not working, I don’t have any errors, just nothing displayed.

I posted a message there but I’m a little in a hurry, so I came here. I hope you’ll find the error in this code!

Main Loop:


void Scene::mainLoop() {
    // Matrices
    mat4 projection;
    projection = perspective(70.0, (double) m_winWidth / m_winHeight, 0.1, 100.0);
    mat4 modelview = mat4(1.0);

    // Camera
    Camera camera(vec3(3, 3, 5), vec3(0, 0, 0), vec3(0, 1, 0), 0.5, 0.5);

    // Init FreeType
    m_output.initShader();
    m_output.initFreeType();

    // Enable Blend
    glEnable(GL_CULL_FACE);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // MAIN LOOP
    while (!m_input.getEndProgram()) {
        // Setting loop started time
        m_loopStart = SDL_GetTicks();

        // Events handling
        m_input.updateEvents();

        // Move mouse function
        camera.move(m_input);

        // Escape - Exit program
        if (m_input.getKeyCode(SDL_SCANCODE_ESCAPE))
            break;

        // Screen clearing
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Camera handling
        camera.lookAt(modelview);

        m_output.RenderText("This is sample text", 50.0f, 50.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
        m_output.RenderText("This is sample text2", 50.0f, -50.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
        m_output.RenderText("This is sample text3", 500.0f, 475.0f, 0.5f, glm::vec3(0.5, 0.8f, 0.2f));
        m_output.RenderText("This is sample text4", 0.0f, 0.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));

        // Window updating
        SDL_GL_SwapWindow(m_window);

        // Check FPS
        calculateFPS(m_fps, m_frameCount, m_currentTime, m_previousTime);

        // Check execution time
        checkElapsedTime();
    }
}

Output class (header):


#ifndef DEF_OUTPUT
#define DEF_OUTPUT

// Windows includes
#ifdef WIN32
#include <GL/glew.h>
#include <SDL2/SDL_image.h>
#endif

// GLM Includes
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include <iostream>
#include <map>
#include <string>
#include <vector>

// File include
#include "shader.h"

#include <ft2build.h>
#include FT_FREETYPE_H

#ifndef BUFFER_OFFSET
    #define BUFFER_OFFSET(offset) ((char*)NULL + (offset))
#endif

class Output {
    public:

    // Constructors and destructors
    Output();
    ~Output();

    // Method
    void initFreeType();
    void initShader();
    void RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color);

    private:

    struct Character {
        GLuint TextureID;   // ID handle of the glyph texture
        glm::ivec2 Size;    // Size of glyph
        glm::ivec2 Bearing; // Offset from baseline to left/top of glyph
        GLuint Advance;     // Offset to advance to next glyph
    };

    // Windows parameters
    int m_winWidth;
    int m_winHeight;

    Shader m_shader;
    glm::mat4 m_orthoMat;

    GLuint m_vbo;
    GLuint m_vao;

    std::map<GLchar, Character> Characters;
};
#endif

initFreeType function:


void Output::initFreeType() {
    // Init freetype
    FT_Library ft;
    if (FT_Init_FreeType(&ft)) {
        std::cout << "Could not init freetype library!" << std::endl;
    }

    // Init font
    FT_Face face;
    if (FT_New_Face(ft, "arial.ttf", 0, &face)) {
        std::cout << "Could not open font!" << std::endl;
    }

    // Set size
    FT_Set_Pixel_Sizes(face, 0, 48);

    // Disable byte-alignment restriction
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    for (GLubyte c = 0; c < 128; c++) {
        // Load character glyph
        if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
            std::cout << "Failed to load Glyph" << std::endl;
            continue;
        }

        // Generate texture
        GLuint texture = 0;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D,
                     0,
                     GL_RED,
                     face->glyph->bitmap.width,
                     face->glyph->bitmap.rows,
                     0,
                     GL_RED,
                     GL_UNSIGNED_BYTE,
                     face->glyph->bitmap.buffer);

        // Set texture options
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        // Now store character for later use
        Character character = {
            texture,
            glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
            glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
            face->glyph->advance.x
        };
        Characters.insert(std::pair<GLchar, Character>(c, character));
    }

    // Release
    FT_Done_Face(face);
    FT_Done_FreeType(ft);

    // Initialize VAO and VBO

    // Deleting a potential former VBO
    if(glIsBuffer(m_vbo) == GL_TRUE)
        glDeleteBuffers(1, &m_vbo);

    // Generate VBO ID
    glGenBuffers(1, &m_vbo);

    // Binding VBO
    glBindBuffer(GL_ARRAY_BUFFER, m_vbo);

    // Allocate video memory
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 24, 0, GL_DYNAMIC_DRAW);     // Changed NULL to 0

    // Unbinding VBO
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // Deleting a potential former VAO
    if(glIsVertexArray(m_vao) == GL_TRUE)
        glDeleteVertexArrays(1, &m_vao);

    // Generate VAO ID
    glGenVertexArrays(1, &m_vao);

    // Binding VAO
    glBindVertexArray(m_vao);

    // Binding VBO
    glBindBuffer(GL_ARRAY_BUFFER, m_vbo);

    // Access vertices in the VRAM
    glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
    glEnableVertexAttribArray(3);

    // Unbinding VBO
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // Unbinding VAO
    glBindVertexArray(0);
}

RenderText function:


void Output::RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color) {
    if (!m_isShaderLoaded)
        return;

    // Activate corresponding render state
    glUseProgram(m_shader.getProgramID());

    // Send colour
    glUniform3f(glGetUniformLocation(m_shader.getProgramID(), "textColor"), color.x, color.y, color.z);
    glActiveTexture(GL_TEXTURE0);

    // Send ortho matrix
    m_shader.sendMat4("projection", m_orthoMat);

    glBindVertexArray(m_vao);

    // Iterate through all characters
    std::string::const_iterator c;
    for (c = text.begin(); c != text.end(); c++) {
        Character ch = Characters[*c];

        GLfloat xpos = x + ch.Bearing.x * scale;
        GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;

        GLfloat w = ch.Size.x * scale;
        GLfloat h = ch.Size.y * scale;

        // Update VBO for each character
        GLfloat vertices[6][4] = {
            {xpos, ypos + h, 0.0, 0.0},
            {xpos, ypos, 0.0, 1.0},
            {xpos + w, ypos, 1.0, 1.0},

            {xpos, ypos + h, 0.0, 0.0},
            {xpos + w, ypos, 1.0, 1.0},
            {xpos + w, ypos + h, 1.0, 0.0}
        };

        // Render glyph texture over quad
        glBindTexture(GL_TEXTURE_2D, ch.TextureID);

        // Update content of VBO memory
        glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        // Render quad
        glDrawArrays(GL_TRIANGLES, 0, 6);

        // Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
        x += (ch.Advance >> 6) * scale;     // Bitshift by 6 to get value in pixels (2^6 = 64)
    }

    // Unbindings
    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);

    // Exit shader program
    glUseProgram(0);
}

Vertex Shader:


// GLSL Version
#version 330 core

layout(location = 0) in vec4 vertex;   // <vec2 pos, vec2 tex>

out vec2 TexCoords;

uniform mat4 projection;

void main() {
    gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
    TexCoords = vertex.zw;
}

Fragment Shader:


// GLSL Version
#version 330 core

in vec2 TexCoords;
out vec4 color;

uniform sampler2D text;
uniform vec3 textColor;

void main() {
    vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
    color = vec4(textColor, 1.0) * sampled;
}

Projection matrix initialization:


m_orthoMat = glm::ortho(0.0f, m_winWidth, 0.0f, m_winHeight);

Here is the complete project files in case you’d want it:
https://drive.google.com/open?id=0B1HyjpNmBqd7LU9hWGIxMlV2MlU

Hi,

It’s me again. Since no one answered to my post I’m closing it. The shared files will no longer be available.

Have a nice day.

Michael