FreeType-纹理图集-为什么我的文本呈现为四边形?

时间:2019-04-28 16:48:43

标签: c++ opengl-es glsl freetype texture-atlas

我目前正在尝试改善文字显示效果。渲染每个角色的基本方法是单独工作,但是现在我想通过渲染纹理图集在一次绘制调用中完成所有操作。 纹理图集几乎完成。它确实将文本渲染为四边形,但是我不知道如何解决alpha问题。 值 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 个答案:

答案 0 :(得分:1)

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_NEARESTGL_LINEAR,并且换行模式必须为{ {1}}:

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