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:
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.
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: