FreeType - Атлас текстуры - Почему мой текст отображается как квад? - PullRequest
0 голосов
/ 28 апреля 2019

Я сейчас пытаюсь улучшить мой рендеринг текста.Основной способ, которым вы визуализируете каждый символ отдельно, работает, но теперь я хочу сделать все за один вызов рисования, визуализируя атлас текстуры.Текстурный атлас почти готов.Он отображает текст как квадрат, но я не могу понять, как решить проблему с альфа-версией.Значения R , G и B равны нулю.

Я также пытался GL_ALPHA, GL_RGBA и GL_LUMINANCE.

Примечание : Я работаю над Raspberry Pi и использую OpenGL ES 2.0.

Изображение всех символов шрифта : An image of the text

Шейдеры :

precision mediump float;

attribute vec4 vertex;

varying vec2 textCoord;

void main()
{
   gl_Position = vec4(vertex.xy, 0.0, 1.0);
   textCoord = vertex.zw;
}
precision lowp float; 

varying vec2 textCoord;

uniform sampler2D text;
uniform vec3 textColor;

void main()
{
    lowp vec4 sampled = vec4(1.0, 1.0, texture2D(text, textCoord).ba);
    gl_FragColor = vec4(textColor, 1.0) * sampled;
}

Настройка Атласа текстур :

glGenBuffers(1, &vbo);

if (FT_Init_FreeType(&m_FT)) {
    std::cout << "ERROR: Could not init the FreeType Library" << std::endl;
}

if (FT_New_Face(m_FT, "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 0, &m_Face)) {
    std::cout << "ERROR: This font failed to load." << std::endl;
}

FT_Set_Pixel_Sizes(m_Face, 0, height);

unsigned int roww = 0;
unsigned int rowh = 0;

memset(c, 0, sizeof c); // Set all values to 0

// Find minimum size for a texture holding all visible ASCII characters 
for (int i = 0; i < 128; i++) {
    if (FT_Load_Char(m_Face, i, FT_LOAD_RENDER)) {
        fprintf(stderr, "Loading character %c failed!\n", i);
        continue;
    }
    if (roww + m_Face->glyph->bitmap.width + 1 >= SCREEN_WIDTH) {
        w = std::max(w, roww);
        h += rowh;
        roww = 0;
        rowh = 0;
    }
    roww += m_Face->glyph->bitmap.width + 1;
    rowh = std::max(rowh, m_Face->glyph->bitmap.rows);
}

w = std::max(w, roww);
h += rowh;

// Create a texture that will be used to hold all ASCII glyphs 
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);

GetShader()->Use();
GetShader()->SetInt("text", tex);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

// We require 1 byte alignment when uploading texture data 
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);

// Paste all glyph bitmaps into the texture, remembering the offset 
int ox = 0;
int oy = 0;

rowh = 0;

for (int i = 0; i < 128; i++) {
    if (FT_Load_Char(m_Face, i, FT_LOAD_RENDER)) {
        fprintf(stderr, "Loading character %c failed!\n", i);
        continue;
    }

    if (ox + m_Face->glyph->bitmap.width + 1 >= SCREEN_WIDTH) {
        oy += rowh;
        rowh = 0;
        ox = 0;
    }


    glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, m_Face->glyph->bitmap.width, m_Face->glyph->bitmap.rows, GL_RGBA, GL_UNSIGNED_BYTE, m_Face->glyph->bitmap.buffer);
    c[i].ax = m_Face->glyph->advance.x >> 6;
    c[i].ay = m_Face->glyph->advance.y >> 6;

    c[i].bw = m_Face->glyph->bitmap.width;
    c[i].bh = m_Face->glyph->bitmap.rows;

    c[i].bl = m_Face->glyph->bitmap_left;
    c[i].bt = m_Face->glyph->bitmap_top;

    c[i].tx = ox / (float)w;
    c[i].ty = oy / (float)h;

    rowh = std::max(rowh, m_Face->glyph->bitmap.rows);
    ox += m_Face->glyph->bitmap.width + 1;
}

fprintf(stderr, "Generated a %d x %d (%d kb) texture atlas\n", w, h, w * h / 1024);

GetShader()->Stop();

glBindTexture(GL_TEXTURE_2D, 0);

Функция рисования :

// Set uniforms
    a_Shader->SetVec3("textColor", m_v3Color);

    glActiveTexture(GL_TEXTURE0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);

    point coords[6 * 128];
    int dc = 0;

    const uint8_t *p;

    float sx = 2.0 / SCREEN_WIDTH;
    float sy = 2.0 / SCREEN_HEIGHT;

//  float x = (m_v2Position.x - (SCREEN_WIDTH / 2)) * sx;
    //float y = (m_v2Position.y - (SCREEN_HEIGHT / 2)) * sy;

    float x = -1.f;
    float y = 0.f;

    // Loop through all characters 
    for (int p = 0; p < 128; p++) {
        // Calculate the vertex and texture coordinates 
        float x2 = x + c[p].bl * sx;
        float y2 = -y - c[p].bt * sy;
        float w = c[p].bw * sx;
        float h = c[p].bh * sy;

        // Advance the cursor to the start of the next character 
        x += c[p].ax * sx;
        y += c[p].ay * sy;

        // Skip glyphs that have no pixels 
        if (!w || !h)
            continue;

        coords[dc++] = (point) {
            x2, -y2, c[p].tx, c[p].ty
        };
        coords[dc++] = (point) {
            x2 + w, -y2, c[p].tx + c[p].bw / w, c[p].ty
        };
        coords[dc++] = (point) {
            x2, -y2 - h, c[p].tx, c[p].ty + c[p].bh / h
        };
        coords[dc++] = (point) {
            x2 + w, -y2, c[p].tx + c[p].bw / w, c[p].ty
        };
        coords[dc++] = (point) {
            x2, -y2 - h, c[p].tx, c[p].ty + c[p].bh / h
        };
        coords[dc++] = (point) {
            x2 + w, -y2 - h, c[p].tx + c[p].bw / w, c[p].ty + c[p].bh / h
        };
    }

    // Render glyph texture over quad
    glBindTexture(GL_TEXTURE_2D, tex);

    // Update content of VBO memory
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(coords), coords, GL_DYNAMIC_DRAW); 
    glBindBuffer(GL_ARRAY_BUFFER, 0);

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

    glDisableVertexAttribArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);

    glCheckError(); 

1 Ответ

1 голос
/ 28 апреля 2019

Буфер, который предоставляется m_Face->glyph->bitmap.buffer, является буфером с одним одноцветным каналом.Поскольку используется OpenGL ES, исходный формат текстуры должен быть GL_LUMINANCE.

Укажите двухмерное изображение текстуры с одним (красным) цветным каналом.Выравнивание для ряда текстуры должно быть установлено 1 (см. glPixelStorei).Примечание: по умолчанию установлено значение 4, что не соответствует плотно упакованной текстуре размером 1 байт на пиксель:

glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);

Примечание. Поскольку текстура не является степенью 2, карта mip не может использоватьсятаким образом, функция минимизации текстуры должна быть GL_NEAREST или GL_LINEAR, а режим обтекания должен быть GL_CLAMP_TO_EDGE:

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

Загрузить глифы в текстуру:

glTexSubImage2D(
    GL_TEXTURE_2D, 0, ox, oy, 
    m_Face->glyph->bitmap.width, m_Face->glyph->bitmap.rows,
    GL_LUMINANCE, GL_UNSIGNED_BYTE, m_Face->glyph->bitmap.buffer);

Поскольку внутренний формат текстуры GL_LUMINANCE, образцы должны быть прочитаны по каналу красного, зеленого или синего цвета:

lowp float sampled = texture2D(text, textCoord).r;
gl_FragColor = vec4(textColor, 1.0) * sampled;
...