1
votes

I'm trying to place a square as background for my text, which is rendered by FreeType in OpenGL. But the text somehow cuts through my square like this: enter image description here

See, the arc behind my square is ok. Arc is on 0.0, square is on 1.0, and the text is on 2.0 on Z axis.

This is how I create them:

auto arc2x = new OpenGL::Rendering::Models::Arc(
    Container::Position(pos_x, pos_y, 0), Container::Color::GREEN, 50,
    radius, 90, arc_degree);
arc2x->create();
arc2x->set_rotation(76, 0, 0, 1);

auto text1_back = new OpenGL::Rendering::Models::Quad(
    Container::Position(pos_x, pos_y - radius, -0.1),
    Container::Color::CYAN, 25, 25);
text1_back->create();

auto text1 = new OpenGL::Rendering::Models::Text(
    "8", Container::Position(pos_x - 5, pos_y - radius / 1.5, 2), 22,
    Container::Color::PINK);
text1->create();

My Text class:

// Text.cpp
#include "Text.h"

using namespace OpenGL::Rendering::Models;

Text::Text(const std::string& text, OpenGL::Container::Position position,
           int font_size, OpenGL::Container::Color color) {
    m_font_size = font_size;
    m_scale = 1.0;
    m_text = text;
    float angle = 0;
    this->color.r = color.r;
    this->color.g = color.g;
    this->color.b = color.b;
    this->color.a = color.a;

    matrix.xx = (FT_Fixed)(cos(angle) * 0x10000L);
    matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L);
    matrix.yx = (FT_Fixed)(sin(angle) * 0x10000L);
    matrix.yy = (FT_Fixed)(cos(angle) * 0x10000L);

    this->position.x = position.x;
    this->position.y = position.y;
    this->position.z = position.z;

    if (FT_Init_FreeType(&font)) {
        Log()->critical("Could not initalize Freetype library for fonts.");
    }

    if (FT_New_Face(font, "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf", 0,
                    &face)) {
        Log()->critical("Could not load font. File is missing maybe?");
    }

    FT_Set_Char_Size(face, 0, m_font_size * 64, 300, 300);
    FT_Set_Pixel_Sizes(face, 0, m_font_size);
    if (FT_Load_Char(face, 'X', FT_LOAD_RENDER)) {
        Log()->critical(
            "Could not load a test glyph. The font is corrupted maybe?");
    }

    for (GLubyte c = 0; c < 128; ++c) {
        FT_Set_Transform(face, &matrix, 0);
        if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
            Log()->critical("Could not load glyph \"{}\"", c);
            continue;
        }

        GLuint texture;
        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);

        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        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);

        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));
    }

    FT_Done_Face(face);
    FT_Done_FreeType(font);
}

void Text::create() {
    GLuint vao;
    GLuint vbo;

    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);

    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL,
                 GL_DYNAMIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat),
                          (void*)0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    this->vao = vao;
    this->vbos.push_back(vbo);
    this->set_program(OpenGL::Managers::ShaderManager::get_program("text"));
    this->set_position(position.x, position.y, position.z);
}

void Text::draw() {

    glUseProgram(this->program);
    glUniformMatrix4fv(glGetUniformLocation(this->program, "model_matrix"), 1,
                       false, &model_matrix[0][0]);
    glUniform4f(glGetUniformLocation(this->program, "text_color"), color.r,
                color.g, color.b, color.a);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(this->vao);

    GLfloat temp_x = 0;
    GLfloat temp_y = 0;

    std::string::const_iterator c;
    for (c = m_text.begin(); c != m_text.end(); c++) {
        Character ch = characters[*c];

        GLfloat xpos = temp_x + ch.bearing.x * m_scale;
        GLfloat ypos = temp_y - (ch.size.y - ch.bearing.y) * m_scale;

        GLfloat w = ch.size.x * m_scale;
        GLfloat h = ch.size.y * m_scale;

        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} /**/
        };

        glBindTexture(GL_TEXTURE_2D, ch.texture_id);
        glBindBuffer(GL_ARRAY_BUFFER, this->vbos[0]);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        temp_x += (ch.advance >> 6) * m_scale;
    }

    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);
}

void Text::set_text(const std::string& a_text) {
    if (!a_text.empty()) {
        m_text = a_text;
    } else {
        Log()->info("Cannot set the text. Input seems to be empty.");
    }
}

std::string Text::get_text() { return m_text; }

void Text::set_color(const Container::Color a_color) {
    color.r = a_color.r;
    color.g = a_color.g;
    color.b = a_color.b;
    color.a = a_color.a;
}

And my Quad class:

// Quad.cpp
#include "Quad.h"

using namespace OpenGL;
using namespace Rendering::Models;

Quad::Quad(Container::Position pos, Container::Color color, float width,
           float height) {
    position.x = pos.x;
    position.y = pos.y;
    position.z = pos.z;

    this->color.x = color.r;
    this->color.y = color.g;
    this->color.z = color.b;
    this->color.w = color.a;

    this->width = width;
    this->height = height;
}

Quad::~Quad() {}

void Quad::create() {
    GLuint vao;
    GLuint vbo;

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    std::vector<Container::Vertex> vertices;
    vertices.push_back(
        Container::Vertex(glm::vec3(-1, -1, 0.0), color));
    vertices.push_back(
        Container::Vertex(glm::vec3(-1, 1, 0.0), color));
    vertices.push_back(
        Container::Vertex(glm::vec3(1, -1, 0.0), color));
    vertices.push_back(
        Container::Vertex(glm::vec3(1, 1, 0.0), color));

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Container::Vertex) * 4, &vertices[0],
                 GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Container::Vertex),
                          (void*)0);

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(
        1, 4, GL_FLOAT, GL_FALSE, sizeof(Container::Vertex),
        (void*)(offsetof(Container::Vertex, Container::Vertex::m_color)));

    glBindVertexArray(0);
    this->vao = vao;
    this->vbos.push_back(vbo);
    this->set_program(OpenGL::Managers::ShaderManager::get_program("shape"));
    this->set_scale(width / 2, height / 2, 1.0);
    this->set_position(position.x, position.y, position.z);
}

void Quad::draw() {
    glUseProgram(this->program);
    glUniformMatrix4fv(glGetUniformLocation(this->program, "model_matrix"), 1,
                       false, &model_matrix[0][0]);
    glBindVertexArray(vao);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glBindVertexArray(0);
}

Any ideas why this is happening? If you need more code for diagnosis, I'll add it right away. Thank you so much.

1
That's definitely an issue with depth-testing and rendering order. You seem to be drawing the circle, then the 8, then the square, with the 8 setting the depth buffer to its own depth event in transparent regions. You should render them back-to-front. - Quentin
@Quentin Thank you, but how do I do that? - Reza Hajianpour
No idea, you didn't provide the code where you actually call the draw functions. But that'd be in that area. - Quentin
Draw functions are inside the code I've provided. I add these models to a std::map, iterate over that map, and call the draw() function on each of these objects. Does GL_DYNAMIC_DRAW while rendering the text has anything to do with it? Here's how I add them to my std::map: FSM::models->add_model("arc2x", arc2x); FSM::models->add_model("text1_back", text1_back); FSM::models->add_model("text1", text1); - Reza Hajianpour
The std::map container doesn't maintain insertion order, so when you iterate through it you draw things in the order the map returns them to you. - Banex

1 Answers

2
votes

You are drawing quads with alpha blending. The alpha blending allows the areas around the 8 to be transparent. However, as far as OpenGL is concerned this is just a modification of the fragment colour. The transparent fragments still get written to the depth buffer.

Your draw order appears to be unspecified. Judging by the comments below the question you are using std::map to hold your models. The order you get when iterating over that container will not necessarily be the same as the insertion order. This means you are potentially drawing a 'closer' element first, causing its depth values to get written to the depth buffer and therefore preventing any subsequent 'further away' elements from contributing to the fragment colour.

Unordered drawing is fine (and actually encouraged for performance reasons,) in the vast majority of cases. However, once you start using alpha blended transparency, order becomes important.

For alpha blended 2D elements the general approach to drawing is:

  • Turn off depth testing.
  • Order the elements in your cpu code based on depth.
  • Draw them from back to front (furthest away to closest).