Рендеринг текста по вектору - PullRequest
       103

Рендеринг текста по вектору

0 голосов
/ 20 сентября 2019

Я хочу пронумеровать грани модели, и поэтому я хочу, чтобы индекс граней был нарисован вдоль стороны треугольника.

Чтобы отобразить текст, который я использую Freetype, остальная часть визуализируется с использованием OpenGL.Для реализации рендеринга текста я следовал инструкциям на learnopengl.

void TextRender::RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color, QMatrix4x4 projMatrix, glm::vec3 dir)
{
    QOpenGLFunctions_4_5_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Core>();

    TextRender::_program->bind();

    f->glEnable(GL_BLEND);
    f->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    f->glUniform3f(f->glGetUniformLocation(TextRender::_program->programId(), "textColor"), color.x, color.y, color.z);
     TextRender::_program->setUniformValue(_program->uniformLocation("projection"), projMatrix);
     f->glActiveTexture(GL_TEXTURE0);
     f->glBindVertexArray(TextRender::VAO);

     // Iterate through all characters
     std::string::const_iterator c;
     for (c = text.begin(); c != text.end(); c++)
     {
         Character ch = TextRender::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
         f->glBindTexture(GL_TEXTURE_2D, ch.TextureID);
         // Update content of VBO memory
         f->glBindBuffer(GL_ARRAY_BUFFER, TextRender::VBO);
         f->glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData

         f->glBindBuffer(GL_ARRAY_BUFFER, 0);
         // Render quad
         f->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 (divide amount of 1/64th pixels by 64 to get amount of pixels))
     }
     f->glBindVertexArray(0);
     f->glBindTexture(GL_TEXTURE_2D, 0);
     f->glDisable(GL_BLEND);
     TextRender::_program->release();
}

Теперь функция получает точку (x, y), с которой будет начертан нарисованный текст.Я думаю, что мне нужно изменить способ обновления VBO для каждого символа и использовать направление, которое представляет собой вектор вдоль многоугольника, который я хочу нарисовать.Но я не совсем уверен, как этого добиться.

1 Ответ

0 голосов
/ 20 сентября 2019

Вместо создания новых данных массива вершин для каждого символа, используйте одинаковые данные для всех символов.Добавьте матрицу модели в код шейдера:

in vec2 vertexCoord;

uniform mat4 projMatrix;
uniform mat4 modelMatrix;

void main()
{
    // [...]  

    gl_Position = projMatrix * modelMatrix * vec4(vertexCoord.xy, 0.0, 1.0);
}

Для каждого символа рисуется прямоугольник от (0, 0) до (1, 1).Установите матрицу модели для каждого глифа, которая

  • масштабирует символ до его размера
GLfloat   w      = ch.Size.x * scale;
GLfloat   h      = ch.Size.y * scale;
glm::mat4 scaleM = glm::scale(glm::mat4(1.0f), glm::vec3(w, h, 1.0f));
  • перемещает символ на позицию в тексте (char_x увеличивается для каждого символа)
GLfloat   xrel      = char_x + ch.Bearing.x * scale;
GLfloat   yrel      = (ch.Size.y - ch.Bearing.y) * scale;
glm::mat4 transRelM = glm::translate(glm::mat4(1.0f), glm::vec3(xrel, yrel, 0.0f));
  • поворачивает текст
GLfloat   angle_rad = atan2(dir.y, dir.x);
glm::mat4 rotateM   = glm::rotate(glm::mat4(1.0f), angle_rad, glm::vec3(0.0f, 0.0f, 1.0f));
  • перемещает текст в его начало
glm::mat4 transOriginM = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, 0.0f));

Окончательная матрица модели:

glm::mat4 modelM = transOriginM * rotateM * transRelM * scaleM;

Примените это к своему коду, но не обязательно, чтобы при каждом вызове * 1030 обновлялся буфер массива вершин.,Достаточно один раз инициировать буфер:

void TextRender::RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color, QMatrix4x4 projMatrix, glm::vec3 dir)
{
    QOpenGLFunctions_4_5_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Core>();

    TextRender::_program->bind();

    f->glEnable(GL_BLEND);
    f->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    f->glUniform3f(f->glGetUniformLocation(TextRender::_program->programId(), "textColor"), color.x, color.y, color.z);
    TextRender::_program->setUniformValue(_program->uniformLocation("projection"), projMatrix);
    f->glActiveTexture(GL_TEXTURE0);
    f->glBindVertexArray(TextRender::VAO);

    GLfloat vertices[6][4] = {
        { 0.0, 1.0,   0.0, 0.0 },
        { 0.0, 0.0,   0.0, 1.0 },
        { 1.0, 0.0,   1.0, 1.0 },

        { 0.0, 1.0,   0.0, 0.0 },
        { 1.0, 0.0,   1.0, 1.0 },
        { 1.0, 1.0,   1.0, 0.0 }
    };

    // Update content of VBO memory
    f->glBindBuffer(GL_ARRAY_BUFFER, TextRender::VBO);
    f->glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData
    f->glBindBuffer(GL_ARRAY_BUFFER, 0);

    GLfloat   angle_rad    = atan2(dir.y, dir.x);
    glm::mat4 rotateM      = glm::rotate(glm::mat4(1.0f), angle_rad, glm::vec3(0.0f, 0.0f, 1.0f));
    glm::mat4 transOriginM = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, 0.0f));

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

          GLfloat w    = ch.Size.x * scale;
          GLfloat h    = ch.Size.y * scale;
          GLfloat xrel = char_x + ch.Bearing.x * scale;
          GLfloat yrel = (ch.Size.y - ch.Bearing.y) * scale;

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

          glm::mat4 scaleM    = glm::scale(glm::mat4(1.0f), glm::vec3(w, h, 1.0f));
          glm::mat4 transRelM = glm::translate(glm::mat4(1.0f), glm::vec3(xrel, yrel, 0.0f));

          glm::mat4 modelM = transOriginM * rotateM * transRelM * scaleM;
          QMatrix4x4 qtModelM(glm::value_ptr(modelM));
          TextRender::_program->setUniformValue(_program->uniformLocation("modelMatrix"), qtModelM);

          // Update VBO for each character

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

          // Render quad
          f->glDrawArrays(GL_TRIANGLES, 0, 6);
     } 
     f->glBindVertexArray(0);
     f->glBindTexture(GL_TEXTURE_2D, 0);
     f->glDisable(GL_BLEND);
     TextRender::_program->release();
}

Если используется Математика OpenGL (GLM) для вычисления матрицы (в единственном числе используется метод glm::vec3, так что это должно быть в порядке).Конечно, то же самое можно сделать с помощью Qt QMatrix4x4 (это было бы очень похоже).
В любом случае, для математики glm вы должны включить:

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
...