在OpenGL中为freetype设置模型矩阵时的笔状行为

时间:2018-08-01 20:17:35

标签: c++ opengl graphics

这就是问题。我正在尝试使用Freetype创建一个Text类,该类继承自Model类,该类又包含诸如设置VAO,VBO,程序,纹理,位置,旋转和缩放等内容。这是Model.cpp的代码:

// Model.cpp
#include "Model.h"

using namespace OpenGL::Rendering;

Model::Model() {
    model_matrix = glm::mat4(1.0f);
    position = glm::vec3(0.0, 0.0, 0.0);
    scale = glm::vec3(1.0, 1.0, 1.0);
    rotation = glm::vec3(1.0, 0.0, 0.0);
    rotation_angle = 0.0;
}

Model::~Model() { destroy(); }

void Model::draw() {}

void Model::set_program(GLuint program) { this->program = program; }

GLuint Model::get_vao() const { return vao; }

const std::vector<GLuint>& Model::get_vbos() const { return vbos; }

GLuint Model::get_texture(std::string texture_name) const {
    if (textures.size() > 0) {
        return textures.at(texture_name);
    } else {
        Log()->critical("No textures to get. Requested {}", texture_name);
        return 0;
    }
}

GLuint Model::get_texture() const {
    if (textures.size() > 0) {
        return textures.begin()->second;
    } else {
        Log()->critical("No textures to get.");
        return 0;
    }
}

void Model::set_texture(std::string texture_name, GLuint texture) {
    if (texture == 0) {
        Log()->critical("Texture {} is empty.", texture_name);
        return;
    } else {
        textures[texture_name] = texture;
    }
}

void Model::destroy() {
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(vbos.size(), &vbos[0]);
    vbos.clear();

    if (textures.size() > 0) {
        for (auto& t : textures) {
            glDeleteTextures(1, &t.second);
        }

        textures.clear();
    }
}

const glm::vec3& Model::get_position() const { return position; }

void Model::set_position(float pos_x, float pos_y, float pos_z) {
    position = glm::vec3(pos_x, pos_y, pos_z);
    update_model_matrix();
}

void Model::set_rotation(float angle_deg, float x, float y, float z) {
    rotation_angle = angle_deg * M_PI / 180.0;
    rotation = glm::vec3(x, y, z);
    update_model_matrix();
}

void Model::set_scale(float sca_x, float sca_y, float sca_z) {
    scale = glm::vec3(sca_x, sca_y, sca_z);
    update_model_matrix();
}

void Model::update_model_matrix() {
    model_matrix = glm::mat4(1.0f);
    auto pos = glm::vec3(this->position.x, -this->position.y, this->position.z);
    glm::mat4 translate_mat = glm::translate(glm::mat4(1.0f), pos);
    glm::mat4 rotate_mat = glm::mat4(1.0f);
    if (this->rotation.x != 0.0 || this->rotation.y != 0.0 || this->rotation.z != 0.0) {
        rotate_mat = glm::rotate(glm::mat4(1.0f), this->rotation_angle, this->rotation);
    } else {
        rotate_mat =
            glm::rotate(glm::mat4(1.0f), 0.0f, glm::vec3(1.0, 0.0, 0.0));
    }

    glm::mat4 scale_mat = glm::scale(glm::mat4(1.0f), this->scale);

    this->model_matrix = translate_mat * rotate_mat * scale_mat;
    glUniformMatrix4fv(glGetUniformLocation(this->program, "model_matrix"), 1,
                       false, &this->model_matrix[0][0]);
}

然后我有我的Text类,该类使用Freetype加载字体和内容。我知道它并没有真正优化,因此请放眼过去。请注意,对于Freetype,我使用的是GL_DYNAMIC_DRAW,而不是GL_STATIC_DRAW

// 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);
    Log()->warn("Pos {0}, {1}, {2}", position.x, position.y, position.z);
}

void Text::draw() {
    GLfloat temp_x = 0;
    GLfloat temp_y = 0;
    glUseProgram(this->program);
    glUniform4f(glGetUniformLocation(this->program, "text_color"), color.r,
                color.g, color.b, color.a);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(this->vao);

    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;
    }
    glUniformMatrix4fv(glGetUniformLocation(this->program, "model_matrix"), 1,
                       false, &model_matrix[0][0]);
    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;
}

所以问题是,绘制文本的行为就像笔一样。因此,当我尝试设置text1对象的位置时,text2对象将移动。 text2影响text3等。

// main.cpp
// This one moves to 200, 100 as defined in text3
auto text1 = new OpenGL::Rendering::Models::Text(
    "Text1", Container::Position(pos_x - 5, pos_y - radius, 0), 18,
    Container::Color::YELLOW);
text1->create();
text1->set_scale(3, 3, 3);
text1->set_position(500, 500, 0);
// This line actually affects text1, as expected
text1->set_text("Blah");

// This one scales to 3 times bigger, as stated in the previous object
auto text2 = new OpenGL::Rendering::Models::Text(
    "Text2", Container::Position(pos_x - radius - 10, pos_y + radius/1.5, 0), 18,
    Container::Color::YELLOW);
text2->create();
text2->set_rotation(25, 0, 0, 1);

// And this one gets rotated 
auto text3 = new OpenGL::Rendering::Models::Text(
    "Text3", Container::Position(0, 0, 0), 125,
    Container::Color::YELLOW);
text3->create();
text3->set_position(200, 100, 0);

这是它的样子: Screenshot of the issue

有人可以解释这个奇怪的行为吗?问题是,所有其他2D形状都可以与我的函数正常工作,但Text类除外,这会影响下一个对象。提前非常感谢您。如果您需要更多信息来诊断问题,我会立即添加。

1 个答案:

答案 0 :(得分:1)

如@ Rabbid76的评论中所述,将以下行移动到Text::draw()的开头即可解决此问题:

glUniformMatrix4fv(glGetUniformLocation(this->program, "model_matrix"), 1,
                   false, &model_matrix[0][0]);